3

次の形式をとる可変個引数関数をSchemeで定義する必要があります (define (n-loop procedure [a list of pairs (x,y)])。ペアのリストは任意の長さにすることができます。

各ペアは、下限と上限を指定します。つまり、次の関数呼び出し(n-loop (lambda (x y) (inspect (list x y))) (0 2) (0 3))が生成されます。

(list x y) is (0 0)
(list x y) is (0 1)
(list x y) is (0 2)
(list x y) is (1 0)
(list x y) is (1 1)
(list x y) is (1 2)

明らかに、carとcdrは私のソリューションに関与する必要があります。しかし、これを困難にする規定は次のとおりです。割り当てステートメントまたは反復ループ(whileおよびfor)はまったく使用されません。

whileとforを使用してペアのリストをインデックスに登録することはできますが、再帰を使用する必要があるようです。説明に必要だと思わない限り、コードソリューションは必要ありませんが、これがどのように攻撃される可能性があるかについて誰かが提案していますか?

4

1 に答える 1

4

Schemeでループを行う標準的な方法は、末尾再帰を使用することです。実際、次のループがあるとしましょう。

(do ((a 0 b)
     (b 1 (+ a b))
     (i 0 (+ i 1)))
    ((>= i 10) a)
  (eprintf "(fib ~a) = ~a~%" i a))

これは実際には次のようなものにマクロ展開されます。

(let loop ((a 0)
           (b 1)
           (i 0))
  (cond ((>= i 10) a)
        (else (eprintf "(fib ~a) = ~a~%" i a)
              (loop b (+ a b) (+ i 1)))))

さらに、これにマクロ展開されます(これはcond私のポイントとは無関係なので、マクロ展開はしません):

(letrec ((loop (lambda (a b i)
                 (cond ((>= i 10) a)
                       (else (eprintf "(fib ~a) = ~a~%" i a)
                             (loop b (+ a b) (+ i 1)))))))
  (loop 0 1 0))

ここを見てletrec、「ああ!再帰が見える!」と考えているはずです。実際にそうします(特にこの場合、末尾再帰ですletrecが、非末尾再帰にも使用できます)。

Schemeの反復ループは、そのように書き直すことができます(名前付きletバージョンは、Schemeでループが慣用的に記述される方法ですが、割り当てで名前付きを使用できない場合はlet、さらに1ステップ展開して、を使用しますletrec)。上で説明したマクロ拡張は単純で機械的であり、一方が他方にどのように変換されるかを確認できるはずです。


あなたの質問は可変個引数関数についてどうですかと尋ねたので、次のように可変個引数関数を書くことができます:

(define (sum x . xs)
  (if (null? xs) x
      (apply sum (+ x (car xs)) (cdr xs))))

(これは、ところで、sum関数を書くためのひどく非効率的な方法です。私はapply、任意の数の引数を送信(使用)および受信(不適切なラムダリストを使用)する方法を示すために使用しています。)


アップデート

さて、ここにいくつかの一般的なアドバイスがあります:あなたは2つのループが必要になります:

  1. 範囲レベルを通過する外側のループ(それはあなたの可変個引数のものです)
  2. 各範囲レベルの数値をループする内部ループ

これらの各ループでは、次のことについて慎重に検討してください。

  1. 開始条件は何ですか
  2. 終了条件は何ですか
  3. 各反復で何をしたいのか
  4. 反復間で維持する必要のある状態があるかどうか

特に、最後のポイントについて慎重に検討してください。これは、任意の数のネストレベルが与えられた場合に、ループをネストする方法です。(以下の私のサンプルソリューションでは、それがcur変数です。)

これらすべてを決定したら、ソリューションの一般的な構造を組み立てることができます。私のソリューションの基本構造を以下に投稿しますが、私のコードを見る前に、問題をどのように解決したいかをよく考えておく必要があります。これにより、間にどのような違いがあるかをよく理解できます。あなたのソリューションアプローチと私のもの、そしてそれはあなたが私のコードをよりよく理解するのを助けるでしょう。

doまた、最初に命令型ループ(のように)を使用して記述し、次にすべてが機能しているときに名前が付けられた同等のループに変換することを恐れないでくださいlet。最初のセクションを読み直して、その変換を行う方法を確認してください。

そうは言っても、これが私の解決策です(詳細は削除されています):

(define (n-loop proc . ranges)
  (let outer ((cur ???)
              (ranges ranges))
    (cond ((null? ranges) ???)
          (else (do ((i (caar ranges) (+ i 1)))
                    ((>= i (cadar ranges)))
                  (outer ??? ???))))))

これが機能するようになったら、doループをnamedに基づくループに変換する必要があることを忘れないでくださいlet。(または、さらに進んで、外側と内側の両方のループをそれらのletrec形式に変換する必要がある場合があります。)

于 2012-10-08T12:12:03.150 に答える