4

おそらくすべての経験豊富な elispers がある時点で発見したように、次のようなコードは壊れています。

(let ((a 3)
      (b 4)
      (c (+ a b)))
  c)

let*バインディング句内でバインドされたばかりの変数を参照する場合は、代わりにフォームを使用する必要があります。

私はただ疑問に思っています - 一見間違った振る舞いがデフォルトになっているのはなぜですか? どのように使用するかに関係なく、常に選択することにリスクはありますlet*か?

4

4 に答える 4

22

を使用して、任意let*の形式を簡単に(そして機械的に)表現できますlet。たとえば、特別な形式がなければ、次のようにlet*表現できます。

(let* ((a 3)
       (b 4)
       (c (+ a b)))
  c)

なので:

(let ((a 3))
  (let ((b 4))
    (let ((c (+ a b)))
      c)))

一方、一部のlet形式は、可能であれば、を使用して表現するのが非常に困難let*です。次の形式は、追加の一時変数または難解なビット演算を導入せずletにを使用して表現することはできません。let*

(let ((x y) (y x)) ; swap x and y
  ...)

letしたがって、この意味では、より基本的だと思いますlet*

于 2012-08-06T01:28:51.320 に答える
6

理由は主に歴史的なものであると教えられましたが、それでも当てはまる可能性があります。let並列割り当てが行われるため、変数間にデータの依存関係がないため、コンパイラーは速度またはスペースを柔軟にコンパイルし、よりもはるかに高速にコンパイルできますlet*。elispコンパイラがこれをどれだけ使用しているかわかりません。

少しグーグルすると、CommonLispの同様の質問が明らかになります:CommonLispのLETとLET*

于 2012-08-07T08:57:55.310 に答える
4

そうするのは正しくない

(let ((a 10)
      (b a)) <- error
  b)

そうするのは正しいです:

(let* ((a 10)
       (b a))
   b) <- will evaluate to 10.

最初のケースのエラーは、let のセマンティクスによるものです。

let*新しいシンボルを既存の環境に追加し、その中でそれらを評価します。

let新しい環境を作成し、現在の環境で新しいシンボルを評価します。新しい環境は、すべての新しいシンボルの評価の最後に責任を負い、(let () code ) のコードを評価します。

let*のシンタックスシュガーです

(let ((a xxx))
  (let ((b a)) ...)

whileletシンタックス シュガーです

((lambda (a)
  ...)
  val_a)

2 番目の形式はより一般的であるため、おそらく短い名前を付けることを考えました...

于 2012-08-06T01:14:07.977 に答える
2

let壊れたと呼ぶかどうかはわかりません。たとえば、何らかの理由で、囲んでいる式に関して定義しているときに、囲まれた環境でシャドウイングaしたい場合があります。bc

(let ((a 11)
      (b 12))
  (let ((a 3)
        (b 4)
        (c (+ a b)))
    ;; do stuff here will shadowed `a' and `b'
    c))
> 23

リスクについては、それ自体はわかりません。しかし、必要がないのに、なぜ複数のネストされた環境を作成したのでしょうか。そうすることでパフォーマンスが低下する可能性があります(私はelispの経験が十分ではありません)。

于 2012-08-06T01:31:05.330 に答える