3

私はコンピュータープログラムの構造と解釈に従っています.Ex 1.3を解決しようとしているときに、最初の試みとして次のコードにたどり着きました:

(define (sumsq a b c)(
(define highest (if (> (if (> a b) a b) (if (> a c) a c)) (if (> a b) a b) (if (> a c) a c)))
(define second_h (if (> (if (> a b) b a) (if (> a c) c a)) (if (> a b) b a) (if (> a c) c a)))
(+ (* highest highest) (* second_h second_h)))

うまくいかなかったので、解決策を調べてSICP Wikiで見つけました

;; ex 1.3
;; implemented using only techniques covered to this point

(define (square x) (* x x))

(define (sum-of-squares x y)
  (+ (square x) (square y)))

(define (largest-two-of-three x y z)
  (if (>= x y)
      (sum-of-squares x (if (>= y z) y z))
      (sum-of-squares y (if (>= x z) x z))))

違いは、複数のステートメントを使用して変数を定義し、2 乗を合計していたことですが、正しい方法は各行を関数として定義することです。

スキームの関数はワンライナーですか? それとも私はすべてを逃したのですか?

4

5 に答える 5

6

プログラム フローの概要を把握するには、適切なインデントと改行を使用する必要があります。最初の提案は次のようになります。

(定義 (sumsq abc)
  ((最高を定義する
     (if (> (if (> ab) ab)
            (if (> ac) ac))
         (if (> ab) ab)
         (もし (> ac) ac)))
   (秒-h を定義する
     (if (> (if (> ab) ba)
            (もし (> ac) ca))
         (if (> ab) ba)
         (もし (> ac) ca)))
   (+ (* 最高 最高)
      (* 秒-h 秒-h)))

最初に注意すること: 括弧が一致しません。閉じたものよりも開いたものが 1 つあります。注意深く調べると、2 行目の左括弧の 1 つが間違っていることがわかります。ちなみに、これは最初の行の最後にぶら下がっていたものです。あなたがこれを評価しようとしたとき、読者はステートメントの終わりを待っていたので、何も起こらなかったと思います.

適切なインデントは非常に重要です。例は一般的にこのように行われますが、SICPはそれを明示的に説明していないと思います。ここでスタイルガイドを見つけました。

2番目の観察:あなたは自分自身を何度も繰り返します. これらすべてのネストされたifステートメントで、本当に正しい値を取得したかどうかはよくわかりません。見つけた解決策を見て、これがどのように大幅に簡素化されるかを確認してください。

サブ結果に名前を付けて、複雑さを解消しようとしました。複雑さを分割することは良いことですが、一般的には、結果ではなく概念に名前を付ける方が適切です。あなたが何をしているかを考えてから、これらの活動に名前を付けてください。これらは関数であり、最終的に問題をほとんど自明に解決する言語を構成します。

于 2009-02-02T15:02:19.650 に答える
5

あなたが書いたもの(マイナス1つの余分なパレン)は次のとおりです。

(define (sumsq a b c)
  (define highest
    (if (> (if (> a b) a b)
           (if (> a c) a c))
      (if (> a b) a b)
      (if (> a c) a c)))
  (define second_h
    (if (> (if (> a b) b a)
           (if (> a c) c a))
      (if (> a b) b a)
      (if (> a c) c a)))
  (+ (* highest highest) (* second_h second_h)))

彼らの解決策は、二乗と二乗和を別々の関数に分離しますが、それが重要だとは思いません。記述しないと、計算している2つの値に名前を付ける必要がなくなり、最後に1つの大きな式として関数を記述できるようになりますが、今はもっと大きな問題があります(+ (* a a) (* b b))

あなたが抱えている問題は、あなたの(もし...)表現が大きすぎて簡単に理解できないことだと思います。何度も現れる2つのパターンがあることに注意してください:(if (> a b) a b)(if (> a b) b a)。これらはmax関数とmin関数であるため、次のように定義すると便利です。

(define (min a b) (if (< a b) a b))
(define (max a b) (if (< a b) b a))

このようにして、ソリューションを次のように書き直すことができます。

(define (sumsq a b c)
  (define highest
    (if (> (max a b) (max a c))
      (max a b)
      (max a c)))
  (define second_h
    (if (> (min a b) (min a c))
      (min a b)
      (min a c)))
  (+ (* highest highest) (* second_h second_h)))

もう一度単純化すると、次のようになります。

(define (sumsq a b c)
  (define highest
    (max (max a b) (max a c)))
  (define second_h
    (max (min a b) (min a c)))
  (+ (* highest highest) (* second_h second_h)))

(max (max a b) (max a c))この記述は、との最大値でa bありc、実際にはと書き直すことができるため、推論がはるかに簡単であることに注意して(max (max a b) c)ください。しかし、見てみるとsecond_h、それが正しいかどうかは明らかではありません。a3つの値のうち最小のものがあればどうなりますか?

彼らがソリューションで使用する秘訣は、最初にとを比較することxですy。の場合x < y、それは3つのうち最小ではないことがわかってyいるため、最も高いか2番目に高いかのいずれかです。x使用するもう1つの数値は、との高い方です。zこれら2つの低い方が、無視したい3つの中で最も小さい数値になるためです。同様のロジックは、の場合にも適用されy < xます。

于 2009-02-02T15:16:58.093 に答える
4

あなたのソリューションは次の形式でした: (define (func param) (define...) (define...))

ただし、define には次の形式が必要です: (define (func param) body)

本体は関数の実装です...それが何をするか、何を返すかです。あなたの体は単なる定義であり、何もしませんでした。これで、Scheme インタープリターがソリューションを受け入れなかった理由が説明されました。

「スキーム関数はワンライナーですか?」という質問に答えるには 次のような「begin」フォームを調べる必要があります: (begin (+ 1 1) (+ 2 2)) => 4

上記の例では、(+ 1 1) の結果はスローされているだけなので、begin の内部に副作用がある場合にのみ本当に意味があることがわかります。

Scheme のいくつかの部分 (特に let と lambda) は、それらの本体の周りに暗黙的な開始点があることに注意してください。したがって、これは有効です:

  (let ((x 1))
    (+ 1 1)
    (+ 2 2))

始まりがなくても。これにより、コードの記述が簡単になります。

最後に、Scheme の学習を続けるときは、開始も副作用もなしに何かを行う方法を常に見つけるようにしてください。特にほとんどのScheme本の最初の数章で、「その変数を設定したい、それからこれをやりたい、そしてこれを…」と考えているなら、おそらく古いプログラミング方法にとらわれて何もしていないでしょう。それはスキームの方法です。副作用にはまったく問題はありませんが、副作用を多用すると、Scheme が最適に機能するようにプログラミングできていないことになります。

于 2009-02-02T13:39:32.813 に答える
4

Scheme のアイデアの 1 つは、bottom-up programming概念的な操作ごとに関数を作成することです。これは、多くの関数型プログラミング言語で推奨されるアプローチです。

このアプローチでは、引数に対して 1 つの論理演算を実装する多数の小さな関数が作成されます。そうすれば、コードはよりモジュール化され、クリーンになります。

于 2009-02-02T12:05:34.483 に答える
1

演習 1.3 では、引数として 3 つの数値を取り、2 つの大きい数値の 2 乗和を返す手続きを定義するように求められます。組み込みの Scheme プロシージャsquaremax、およびを使用してそのようなプロシージャを定義するのは簡単minですが、この本のその時点ではまだこれらのプロシージャに遭遇していないので、それらも定義します。

(define (square x)
   (* x x))

(define (max x y)
   (if (> x y) x y))

(define (min x y)
   (if (< x y) x y))

(define (sum-of-highest-squares x y z)
   (+ (square (max x y))
      (square (max (min x y) z))))

このsum-of-highest-squares手順は、x と y の最大値の 2 乗 (これら 2 つの最大値は 3 つの最小値から除外されます) と、残りの 2 つの最大値の 2 乗 (x と y の最小値、つまり、最初のステップから残った値のいずれか)、および z.

注: これは私のブログ投稿SICP Exercises 1.1 - 1.5からのものです。他の多くの SICP ソリューションへのリンクもあります。

于 2011-01-18T13:55:16.943 に答える