関数が既にあると想像してください。それは何を得るのですか?それは何を生み出す必要がありますか?
与えられたアトムは何を返しますか? アトムの単純なリストが与えられた場合、何を返す必要がありますか?
(defun condense (x)
(typecase x
(number
; then what?
(condense-number x))
(list
; then what?
(if (all-atoms x)
(condense-list-of-atoms x) ; how to do that?
(process-further-somehow
(condense-lists-inside x))))
; what other clauses, if any, must be here?
))
何をしなければなりcondense-lists-inside
ませんか?あなたの説明によると、内部のネストされたリストをそれぞれnumberに凝縮し、アトムをそのままにしておくことです。したがって、数字のリストが残ります。どういうわけかそれをさらに処理するために、私たちはすでに関数を「持っています」、そうですか?condense-list-of-atoms
さて、実装方法はcondense-lists-inside
?簡単だ、
(defun condense-lists-inside (xs)
(mapcar #'dowhat xs))
何をしますか?もちろんcondense
!覚えておいてください、私たちはすでにそれを持っていると想像しています. 意図されたものを手に入れる限り、意図した通りに生産する。つまり、アトムまたはリスト (内部にリストがネストされている可能性があります) を指定すると、 numberが生成されます。
では、空欄を埋めて単純化してください。特に、all-atoms
チェックが本当に必要かどうかを確認してください。
編集:実際にtypecase
は、NIL を LIST として扱うため、使用は残念な選択でした。代わりに「ゼロ値」を返すために、NIL を別の方法で処理する必要があります。したがって、通常の(cond ((null x) ...) ((numberp x) ...) ((listp x) ...) ... )
構成を使用することをお勧めします。
新しいコードについて: エラーが発生しました: の後に返されたアトムのリストを処理するために(mapcar #'condense x)
、それを行う関数calculate
があり、それ自体まで戻る必要はありませんcondense
。そこに置き換えると、チェックがまったく必要ないcalculate
ことが明らかになります。all-atoms
それは、コードの開発を容易にするための教育的な装置に過ぎませんでした。:)正確さの目標を達成した後、それらを単純化して、開発時に余分な選択をしても問題ありません!
ただし、all-atoms
チェックを削除すると、要件 2 が壊れます。すると以下のように計算が進みます
(CONDENSE '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5))
==
(calculate (mapcar #'condense '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5)))
==
(calculate (list 2 3 4 (condense '(3 1 1 1)) (condense '(2 3 (1 2))) 5))
==
(calculate (list 2 3 4 (calculate '(3 1 1 1))
(calculate (list 2 3 (calculate '(1 2)))) 5))
==
(calculate (list 2 3 4 6 (calculate '(2 3 3)) 5))
==
(calculate (list 2 3 4 6 8 5))
==
28
つまり、最も深くネストされたレベルからではなく、左から右に進みます。入れ子になったリストをツリー (ツリー) として想像すると、これはツリーの最も深い左隅から右へと「むしゃむしゃ」になります。チェック付きのコードはall-atoms
、レベルアップによって厳密に続行されます。
したがって、最終的な単純化されたコードは次のとおりです。
(defun condense (x)
(if (listp x)
(reduce #'+ (mapcar #'condense x))
(abs x)))
備考:リダクション シーケンスの最後の図を見ると、引数ツリーの各ノードを計算アプリケーションに置き換えるという明確な図が浮かび上がります。これは、そのままの単純なリストではなく、ツリーに対して行われる折りたたみの明確なケースです。reduce
これは、「car-cdr 再帰」と呼ばれるもので直接コーディングできます。各セルを、セルのコンポーネントへの再帰呼び出しの 2 つの結果に対するcons
結合関数のアプリケーションに置き換えます。f
car
cdr
(defun condense (x) (reduce-tree x #'+ 0))
(defun reduce-tree (x f z)
(labels ((g (x)
(cond
((consp x) (funcall f (g (car x)) (g (cdr x))))
((numberp x) x)
((null x) z)
(T (error "not a number")))))
(g x)))
ご覧のとおり、このバージョンは非常に再帰的であり、あまり良くありません。