今日からラケットを習い始めました。ループを介して追加する正しい方法を見つけようとしていましたが、答えを見つけられなかったり、自分で構文を理解できませんでした。
たとえば、hc-append を使用して 9 つの円の行が必要な場合、ネストされた 9 つの hc-append プロシージャを手動で入力せずにこれを行うにはどうすればよいでしょうか?
最初に理解する必要があるのは、Racket での「ループ」は実際には単なる再帰であることです。この場合、一連の描画呼び出しを連鎖させたいと考えています。これを書き出すと、ターゲットの目標は次のようになります。
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10)
(hc-append (circle 10))))))))))
すべての円が同じ半径になると仮定しています。
さて、再帰的なメソッドを書いていくので、基本ケースを考える必要があります。正確に 9 つの円を描画します。これを円の最大数としましょうmax
。私たちの基本的なケースは、「ループ」から抜け出すときは、max
反復に到達したとき、または(= iterations max)
.
次に、再帰自体について説明します。iterations
現在の iterationと maximum iterationの少なくとも 2 つの変数を渡す必要があることは既にわかっていますmax
。上記のコードを見ると、すべての「ループ」で繰り返される要素が(circle 10)
. これを渡す方法はいくつかあります。たとえば、半径だけを渡すことを選択する人もいますが、最も簡単な方法は、円の写真を渡すことだと思います。
最後に、これまでに行った画像も渡す必要があります。つまり、チェーンに円を追加するときに、追加を続けることができるように、これを再帰メソッドに戻す必要があります。
それを二乗したので、再帰メソッドの構造を定義できます。これを呼び出す必要がありますcircle-chain-recursive
。
(define (circle-chain-recursive iteration max crcle output)
; body here
)
私たちのメソッドの「根性」はif
. 最大反復回数に達した場合は、出力を返します。それ以外の場合は、別の円を追加し、 をインクリメントiteration
して、メソッドを再度呼び出します。
(define (circle-chain-recursive iteration max crcle output)
(if (= iteration max)
output
(circle-chain-recursive
(+ 1 iteration) max crcle (hc-append crcle output))))
個人的には、このような再帰的なループ メソッドを直接呼び出すのは好きではないので、次のようなヘルパー メソッドを記述します。
(define (circle-chain num radius)
(circle-chain-recursive 0 num (circle radius) (circle 0)))
半径 10 の一連の 9 つの円が必要な場合は、 を呼び出すだけ(circle-chain 9 10)
です。
(circle 0)
というパラメーターとして as inを渡したことに気付くでしょうoutput
。これは、hc-append
メソッドにpict
パラメーターが必要なためです。円から始めていないので、「空白」または nil pict に相当するものを渡しました。「空白」のピクトを渡す方法は他にもあるかもしれませんが、私はslideshow/pict
ライブラリに精通していないため、それを知ることができません。
それが少し解消されることを願っています。
Racketで使用できるループには次の3つのスタイルがあります。
このスタイルは、ラケットガイドの第11章「反復と内包表記」で説明されている構文を使用します。次のようになります。
(require slideshow/pict)
(for/fold ([result (blank)]) ([i (in-range 9)])
(hc-append (circle 10) result))
このスタイルでは、forの後の最初の括弧で、ループの一時変数を定義します。私の例には、resultと呼ばれるそのような変数が1つあります。次に、反復変数とそれらが何を反復するかを定義します。したがって、ここでは、iは0から8までの数値をループします。ループの本体はiごとに1回実行され、結果がresultに割り当てられるたびに、ループの最終値は最後のresultの値になります。
このスタイルは、ガイドのセクション3.8で説明されています。次のようになります。
(require slideshow/pict)
(foldl hc-append (blank) (build-list 9 (lambda (i) (circle 10))))
このコードは、9つの円のリストを作成することから始まります。
(define o (circle 10))
(build-list 9 (lambda (i) o) ---> (list o o o o o o o o o)
リスト表記は、より詳細なcons表記の省略形です。
(list o o o o o o o o o) ---> (cons o (cons o (cons o (cons o (cons o (cons o (cons o (cons o (cons o empty)))))))))
foldl関数の考え方は、データの一部を計算の一部に変換することです。リストを入力として受け取り、それを関数呼び出しのコレクションに変換します。
foldl関数はリストを取得し、リスト内の各短所を最初の引数に置き換え、リストの最後にある空を2番目の引数に置き換えます。この例では、 hc-append関数と(空白)を渡すので、置換は次のようになります。
(foldl hc-append (blank) ...) --->
(hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (blank))))))))))
この一連の関数呼び出しは、手作業での書き込みを避けたいものです。Foldlが自動的に計算します。
これは、ロディが彼の答えで説明したスタイルです。コーディングスタイルの一般的な問題として、再帰データ構造をトラバースする場合など、単純な* for、 map、またはfoldが不可能な場合にのみ再帰スタイルを使用する必要があります。
冷凍エンドウ豆のロディは、この答えをかなりカバーしています。
この種のことは非常に一般的なことなので、プロセスを繰り返して結果を蓄積するために、Racket言語にはそのアイデアを簡潔に表現する機能があります。これらの機能の1つは、Racketのfor/foldループです。例として:
> (for/fold ([result 'Go!])
([i (in-range 5)])
(list 'Duck result))
'(Duck (Duck (Duck (Duck (Duck Go!)))))
ここでは、初期値'Go!に対して操作(リスト'ダック結果)を繰り返します。結果を蓄積します。 for / foldの実装は、Roddyが説明する再帰プロセスに基づいているため、基本を理解すれば、for/foldを快適に使用できるようになります。
これは古い投稿ですが、非常に読まれているため、回答を追加しています。「named let」を使用した次の方法も非常に便利です。
(define (f)
(let loop ((counter 1)
(result (blank)))
(if (< counter 10)
(loop (add1 counter)
(hc-append (circle 10) result))
result)))