2013-08-06 9 views
5

seqの各要素を処理するときは、通常firstrestを使用します。 しかし、これにより、引数にseqを呼び出すことによってlazy-seqが "怠惰"を失うことになります。私の解答は、lazy-seqで作業しているときはと(drop 1 coll)を使用していましたが、drop 1はうまくいますが、最初の要素を取得するのに特にfirsttakeを呼ぶ必要はありません。怠惰なseqの最初の要素を得るための慣用句

もっと慣れた方法がありますか? firstrestため

答えて

10

ドキュメンテーション文字列は、これらの機能が言う、のような、それ自体は配列ではありませんseqableコレクションに渡すときにseqを自分で呼び出す必要はありませんアイデアを伝えるためにその引数にseqを呼び出すことを言います、ベクトルまたはセット。 firstは、その引数にseqを呼び出していませんでした場合たとえば、

(first [1 2 3]) 
;= 1 

は動作しないでしょう。その代わりに、

(first (seq [1 2 3])) 

と言う必要があります。これは不便です。

takedropも引数としてseqを呼び出します。そうでない場合は、上で説明したようにベクターなどで呼び出すことができませんでした。実際、これはすべての標準的なseqコレクションに当てはまります。seqを直接呼び出さないコレクションは、下位レベルのコンポーネントに基づいて構築されます。

これは、怠惰なseqの怠惰を損なうことはありません。 first/restコールの結果として起こる強制/実現は、要求された結果を得るために可能な最小量である。 (それは引数のタイプに依存しますが、実際には怠惰でなければ、firstコールには余分な実現はなく、部分的に怠けている - チャンクされている - 余分なものがあります(最大32個の初期要素が一度に計算されます)、完全に怠けている場合は、最初の要素のみが計算されます。

明らかにfirstを渡すと、最初の要素の実現が強制されます。それが全体のポイントです。 restは、実質的には、実際にはseqの「残りの部分」の実現を強制しないという点で、やや怠惰です(これは(seq (rest ...))と基本的に同じであるnextとは対照的です)。最初の要素を即座にスキップできるように、最初の要素を強制的に実行するという事実は、遅延seqオブジェクトの不必要なレイヤリングを回避し、元のseqの先頭を保持する意識的な設計選択です。遅いseqラッパーが実現するまで、xsを保持するコストで、この初期実現を延期するために(lazy-seq (rest xs))のようなものを言うことができます。

+0

ありがとうございました。私は、チャンクされたものと完全に怠惰なものの違いは、私を捨てていたと思う。特にlazy-seqで最初に作業していたときに、私が作業していたものは、チャンクされたときに問題を引き起こしているはずだと思うので、それ以降はそれに固執しました。 – robertjlooby

+0

はい、チャンク化すると予想外の結果につながる可能性があります。 'take 'は実装時に' first'と 'rest'の両方を使いますが、' drop'は 'rest'の上に構築されるので、助けにはなりません。unchunkingは可能ですが、チャンクされたseqの上に階層化された変換が一度に1つの要素だけ評価されるという意味でのみです。基礎となるチャンクされたseqは常にそのチャンクを完全に実現します。 (これを行う方法は、チャンクされたseqを元のseqをクロージャで 'reify clojure.lang.ISeq ... 'とした、unchunking seqでラップすることです) –

+0

' take'/'drop'は実際に' seq'を呼び出しますか? replで試して 'class 'を'(range) '、'(take 1(range)) '、'(drop 1(range)) 'と呼ぶだけで' clojure.lang.LazySeq'となります。 '(rest(range))'、 '(seq(range)) 'を呼び出すと' clojure.lang.ChunkedCons'が返されます。 – robertjlooby

関連する問題