21

リストのAPIチートシートのセクションは、それが'()と同じようにリストコンストラクターであることを示しているよう(list)ですが、実際にはまったく同じではないことがわかりました。たとえば、次のようになります。

(def foo "a")
(def bar "b")
(def zip "c")

次のステートメント:

(apply str '(foo bar zip))

出力「foobarzip」を生成しますが、これは予期していませんでした。

しかし、おそらく同等です:

(apply str (list foo bar zip))

予想通り、「abc」を生成します。

何が起きてる?Clojureのリストの「省略形」({}マップや[]ベクトルなど)がある場合、それは何ですか?

4

3 に答える 3

36

lispsでは、'(like quote)は引数を引用します。つまり、s-exp形式で記述されたとおりに、内部で何も評価しないことを含め、引数をほぼ正確に保持します。

別の言い方をすれば、記号、、;'(foo bar zip)を含むリストを作成します。whileは、、、のを含むリストを作成します。最初のケースでは、シンボル自体を文字列に変換してから連結します。foobarzip(list foo bar zip)foobarzipstr

これのデモンストレーションとして:

=> (def foo "a")
=> (type (first '(foo)))
clojure.lang.Symbol
=> (type (first (list foo))) 
java.lang.String
于 2012-04-24T11:31:38.990 に答える
10

違いは、ご覧のとおり、リテラル構文'()引用符で囲まれていることです。これは、内部のシンボルが評価されないことを意味します。要素の評価中にリストリテラルを使用するには、syntax-quoteリーダーマクロを利用できます。

user=> (apply str `(~foo ~bar ~zip))
"abc"
于 2012-04-24T11:33:35.060 に答える
6

あなたが書くとき:

(def foo "a")
(def bar "b")
(def zip "c")

3つのシンボルを定義しました:foobarおよびzip値に関連付けられています:"a""b"および"c"

アソシエーションはnamsepaceテーブル内に格納されるため、REPLを使用する場合、名前空間はuserデフォルトで使用されるため、ユーザー名前空間テーブルには次のものが含まれます。

{foo
#'user/foo
bar
#'user/bar,
zip
#'user/zip}

次のようにして、名前空間テーブルを表示できます。(ns-interns *ns*)

ここで、次のように記述します(foo bar zip)。Clojure内では、リーダーがこれを読み取ることができる4つの異なる方法があります。どちらの方法で読むかを読者に指定する必要があります。そこで`'そしてlist登場します。

インジケーターがない場合:

(foo bar zip)

インジケーターなしで単純に記述された場合、リーダーはこれをS式として解釈fooし、関数へのシンボルマッピングとして解釈し、関数barzip渡される値へのシンボルマッピングとして解釈しfooます。

この場合、例外がスローされます。

java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn

これは、関数fooが定義されておらず、IFn(Clojure関数)ではなく文字列であるfooに関連付けられていたためです。"a"

関数fooが定義されている場合は、引数および。fooとして渡すことを呼び出します。"b""c"

の場合list

(list foo bar zip)

リスト記号を使用する場合、読者は実際にはこれをインジケーターなしの場合と同じように解釈します。つまり、にマップされた関連する値を引数として受け取る関数にマップされたシンボルを探していますlist。この関数は、clojure.core名前空間内のClojureによって事前定義されています。引数のリストを返します。foobarziplist

したがって、リーダーがをlist検索すると、clojure.core関数が検索され、次にfooシンボルが検索され、にマップされていることがわかります"a"。シンボルのすべてのマッピングが見つかるlistと、関連する値である。を呼び出して渡しfoo bar zipます"a" "b" "c"。だから(list foo bar zip)と同じ(list "a" "b" "c")です。

の場合'

'(foo bar zip)

引用は、次の'フォームがそのまま読まれることを読者に伝えます。この場合foo、、、barおよびzipは記号であり、(foo bar zip)記号のリストです。したがって、読者はこれを記号のリストとして解釈します。

そのため、実行する(apply str '(foo bar zip))と、が呼び出さstr 'foo 'bar 'zipれますfoobarzip。つまり、シンボルのリスト内の各シンボルを文字列表現に変換してから、これらを1つの文字列に連結します。

そのままの形をとることにより、シンボルを評価することなく、つまり、それらが関連付けられているものを探すことなく、引数としてシンボルのリストを渡します。実行した場合は、(eval '(foo bar zip))シンボルのリストをに渡し、シンボルを値に評価して、シンボルがマップされている値のリストを返します。したがって、引用符はコードをコードとして渡すものと考えることができます。evaleval'

の場合`

`(foo bar zip)

これはもう少し複雑です。読者はバッククォートを確認し`、シンボルのリスト内のシンボルを再帰的に解決して、完全に修飾されたシンボルのリストを取得します。

基本的に、リーダーがシンボルのテーブル内のシンボルを検索するとき、それは現在の名前空間のシンボルのテーブルから検索します。完全修飾シンボルは、名前空間情報を含むシンボルです。したがって`(foo bar zip)、リーダーを実行すると、これらの記号が完全に修飾された記号に置き換えられ、に変換され(user.foo user.bar user.zip)ます。

リスト内の要素の一部を評価し、他の要素を完全修飾記号に変更するように読者に指示することができます。そのためには、評価するシンボルの前に次のように接頭辞を付けます~

`(foo ~bar zip)

あなたに与える

(clojure.foo "b" clojure.zip)

事実上、バッククォートは、評価しないという点でクォート`と非常によく似ていますが、コード内のシンボルを完全に修飾することによって返されるコードを操作することを除いて、単にコードを返します。'これは、マクロに影響を及ぼします。完全に修飾された参照が必要な場合や、別の名前空間からフェッチする場合があります。また、現在の名前空間でこのシンボルを探すという柔軟性が必要な場合もあります。

于 2015-10-14T00:31:26.017 に答える