web-dev-qa-db-ja.com

LISPコミュニティが関数の最後にすべての括弧を蓄積することを好むのはなぜですか?

LISPコミュニティが関数の最後にすべての括弧を蓄積することを好むのはなぜですか。

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)))

CやJavaのような規則を採用しないのはなぜですか?
まあ、LISPはこれらの言語よりもはるかに古いですが、私は現代のLispersについて話しています。

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

注:コードスニペットは、本「The Joy of Clojure」からの抜粋です。

26
Chiron

ALGOLベースの言語が独自の行で中かっこを推奨する理由の1つは、中かっこを移動せずに、区切り中かっこの間に行を追加することを推奨することです。つまり、最初に

if (pred)
{
  printf("yes");
}

中括弧内に別のステートメントを追加するのは簡単です。

if (pred)
{
  printf("yes");
  ++yes_votes;
}

元の形があった

if (pred)
{ printf("yes"); }

次に、 "移動"two中かっこを使用する必要がありますが、私の例では後者をより重視しています。ここで、中括弧は、ステートメントのシーケンスであることが意図されているものを区切っており、主に副作用のために呼び出されます。

逆に、LISPにはステートメントがありません。すべての形式はexpressionであり、いくつかの値を生成します。まれなケース(Common LISPと考える)でも、その値は意図的に「値なし」に選択されます"空の (values) フォーム。 ネストされた式とは対照的に、式のシーケンスを見つけることはあまり一般的ではありません。 「終了デリミタまで一連のステップを開く」という欲求はそれほど頻繁には発生しません。これは、ステートメントが消えて戻り値がより一般的な通貨になるにつれて、式の戻り値を無視することが少なくなるためです。副作用だけで一連の式を評価することはまれです。

Common LISPでは、 progn 形式は例外です(その兄弟と同様)。

(progn
  (exp-ignored-return-1)
  (exp-ignored-return-2)
  (exp-taken-return))

ここで、prognは3つの式を順番に評価しますが、最初の2つの式の戻り値は破棄します。最後の閉じかっこを独自の行に書くことを想像できますが、最後の形式はここでは特別であるためです(一般的なLISPの意味ではありません special ただし、)明確な扱いにより、発信者が「最後に別の式を追加する」のではなく、シーケンスの途中に新しい式を追加する可能性が高くなります。新しい副作用だけでなく、戻り値の変更の可能性による影響も受けます。

大幅に簡略化すると、LISPプログラムのほとんどの部分の括弧は、Cのような言語と同様に、関数に渡される引数を区切ることであり、ではなくで区切るステートメントブロック。同じ理由で、Cの関数呼び出しを囲む括弧を引数の近くで維持する傾向があるため、LISPでも同じように行い、その近いグループから逸脱するモチベーションを下げます。

括弧を閉じることは、開く括弧のインデントよりもはるかに重要ではありません。やがて、かっこを無視し、Pythonプログラマーが行うように、形状によって読み書きすることを学びます。しかし、その類推があなたにそれを考えさせないようにしてくださいremoving括弧は完全に価値があります。いいえ、それは comp.lang.LISP

28
seh

それは役に立たないので。コード構造を示すためにインデントを使用します。コードのブロックを分離したい場合は、本当に空の行を使用します。

LISP構文は非常に一貫しているので、括弧は、プログラマーとエディターの両方にとってインデントの決定的なガイドです。

(私にとって、問題はむしろCとJavaプログラマーが中括弧を使いたがる理由です。)

実例として、これらの演算子がCのような言語で利用可能であると仮定します。

Foo defer_expensive (Thunk cheap, Thunk expensive) {
    if (Foo good_enough = force (cheap)) {
        return good_enough; }
    else {
        return force (expensive); }}

2つの閉じブレースは、2つのネストレベルを閉じます。構文は明らかに正しいです。 Python構文と同様に、中かっこは明示的なINDENTおよびDEDENTトークンです。

もちろん、これは「真のブレーススタイル」ではないかもしれませんTM、でもそれは単なる歴史的な事故と習慣だと思います。

14
Svante

その場合、コードははるかにコンパクトになります。とにかく、エディター内の動きはs式によるものなので、編集のためにそのスペースは必要ありません。コードは、主に構造と文によって読み取られます-区切り文字に従うことではありません。

11
Rainer Joswig

Lispers、ご存知のように、ありがたいことに悪臭を放ち、そこにisあるje ne sais quoiが2番目の例にあります。

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

最初は、いわば魅力の源に指を置くことができませんでした。それから、トリガーが欠けていることに気づきました。

(defn defer-expensive [cheap expensive]      
  (if-let [good-enough (force cheap)]
    good-enough   ;   )
    (force expensive) 
  )
)

ほら!

LISPのギャンググリップを練習します。

5
Kaz

私の場合、区切り文字専用の行が画面スペースの浪費であることがわかります。Cでコードを記述すると、次のようなスタイルもあります。

if (pred) {
   printf("yes");
   ++yes_votes;
}

スペースを節約するために、開始括弧を「if」の同じ行に配置するのはなぜですか。また、「if」がすでに独自の行を持っている場合に、独自の行があると冗長に見えるからです。

括弧を最後にまとめても同じ結果になります。 Cではステートメントがセミコロンで終わり、括弧のペアがこのように開かないため、奇妙に見えます

{if (pred)
    printf("yes");
}

それは最後のブレースのようなもので、場違いに見えます。このように開きます

if (pred) {
    printf("yes");
}

'{'& '}'でブロックとその制限を明確に表示

そして、vimのように括弧をハイライトして括弧を一致させるエディターの助けを借りて、すべての括弧がパックされている最後に移動し、それらを簡単に移動し、すべての開始括弧を一致させ、ネストされたLISPフォームを確認できます。

(defn defer-expensive [cheap expensive]
    _(if-let [good-enough (force cheap)]
    good-enough
    (force expensive)_))

カーソルを中央の閉じ括弧に置き、if-letの開き括弧を強調表示できます。

0
kisai