45

私はこれについて多くのことを考えてきましたが、それについて何も見つけることができませんでした。

関数を使用すると、実際seqにどのように機能しますか? どこでも、 は を評価し、結果を破棄して を返すと説明されているだけです。seq a bab

しかし、それは本当にどういう意味ですか?次の場合、厳密に評価されますか。

foo s t = seq q (bar q t) where
      q = s*t

つまり、qで使用される前に厳密に評価されbarますか? そして、以下は同等でしょうか:

foo s t = seq (s*t) (bar (s*t) t)

この関数の機能の詳細を取得するのは少し難しいと思います。

4

2 に答える 2

41

あなたは一人じゃない。 seqおそらく、いくつかの異なる理由から、適切に使用するのが最も難しい Haskell 関数の 1 つです。あなたの最初の例では:

foo s t = seq q (bar q t) where
      q = s*t

qが評価される前bar q tに評価されます。が評価されない場合bar q tは、どちらも評価されqません。だからあなたが持っているなら

main = do
    let val = foo 10 20
    return ()

使用さvalれないため、評価されません。なのでq評価もされません。代わりに持っている場合

main = print (foo 10 20)

の結果はfoo 10 20(によってprint) 評価されるため、 withinfoo qは の結果の前に評価されbarます。

これが機能しない理由でもあります。

myseq x = seq x x

意味的には、これは2 番目が評価xされる前に 1 番目xが評価されることを意味します。しかし、2 番目xが評価されない場合、1 番目も評価される必要はありません。Soseq x xは とまったく同じxです。

2 番目の例は、同じものである場合とそうでない場合があります。ここで、式s*tは の出力の前に評価されますが、 への最初のパラメーターbarと同じではない場合があります。コンパイラが共通部分式の削除を実行すると、2 つの同一の式が共通化される場合があります。ただし、GHC は CSE を実行する場所についてかなり保守的である可能性があるため、これに頼ることはできません。私が定義すると、バーでその値を使用する前にCSE を実行して評価します。より複雑な式ではそうではない場合があります。s*tbarbar q t = q*ts*t

また、厳密な評価の意味を知りたい場合もあります。 seq最初の引数を弱いヘッド正規形 (WHNF) に評価します。これは、データ型の場合、最も外側のコンストラクターをアンパックすることを意味します。このことを考慮:

baz xs y = seq xs (map (*y) xs)

xsのため、リストでなければなりませんmap。がそれseqを評価すると、本質的にコードが次のように変換されます。

case xs of
  [] -> map (*y) xs
  (_:_) -> map (*y) xs

これは、リストが空かどうかを判断し、2 番目の引数を返すことを意味します。リスト値は評価されないことに注意してください。だからあなたはこれを行うことができます:

Prelude> seq [undefined] 4
4

しかし、これではありません

Prelude> seq undefined 5
*** Exception: Prelude.undefined

s の最初の引数に使用するデータ型が何であれseq、WHNF への評価は、コンストラクターを理解するのに十分であり、それ以上ではありません。データ型に、強打パターンで厳密とマークされているコンポーネントが含まれている場合を除きます。次に、すべての厳格なフィールドも WHNF に評価されます。

編集:(コメントで提案してくれたDaniel Wagnerに感謝します)

関数の場合seq、関数が「ラムダが表示される」まで式を評価します。これは、関数を適用する準備ができていることを意味します。これが何を意味するかを示すいくつかの例を次に示します。

-- ok, lambda is outermost
Prelude> seq (\x -> undefined) 'a'
'a'

-- not ok.  Because of the inner seq, `undefined` must be evaluated before
-- the lambda is showing
Prelude> seq (seq undefined (\x -> x)) 'b'
*** Exception: Prelude.undefined

ラムダ バインディングを (組み込みの) データ コンストラクターと考えると、seqon 関数はデータでの使用と完全に一致します。

また、「ラムダ バインディング」は、ラムダ表記で定義されているか、通常の関数として定義されているかに関係なく、すべてのタイプの関数定義を包含します。

HaskellWiki の seq ページの論争セクションには、関数に関連する in の結果のいくつかについて少し書かseqれています。

于 2012-06-15T09:27:38.113 に答える