2016-08-24 6 views
1

を動作しないで/私は1つのフィールドの変化とレコード構文を更新する声明、コピーデータ型自体にしましょうので、私のようなものでした:はハスケル:修正なし

let rec = rec{field = 1} 

をしかし、私は気づきました私はもうrecを印刷できないということは、コンパイラが無限ループに陥ることを意味します。私はやって試してみました:

let a = 1 -- prints OK 

let a = a -- now i can't print a (also stuck in a loop) 

だから私はどのようなタイプでlet a = aを行うことはできませんが、私はなぜ理解していない、とどのように私はこの問題を解決する必要があります。ところで

:やっている間:

let b = a {...record changes..} 

let a = b 

作品を、しかし、冗長なようです。

答えて

6

問題が発生しているのは、デフォルトでは、Haskellのすべてletwhereバインディングが再帰的です。あなたは

let rec = rec { ... } 

を書くときだから、永遠にループします(ちょうどlet a = aのように)それを評価しようとした円形のデータ型を定義しようとします。

これは本当の意味ではありません。言語のトレードオフです。再帰的な関数(そして単純な値でさえも)は、ノイズの少ない簡単な書き出しを可能にしますが、それ自体ではaを簡単に再定義することはできません。

あなたが本当に行うことができる唯一の事柄は、あなたの値に異なる名前を与えることです。recrec'はこれを行う一般的な方法です。

ハスケルにとって公平であるためには、再帰関数や再帰的な値さえもかなり頻繁に出てくる。

fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 

ようなコードは、あなたがそれのこつを得れば、本当にいいこと、及び(あなたは、たとえば、内OCamlの操作を行う必要があるだろうように)明示的に再帰的として、この定義をマークしたことは明確逆さまではありません。することができます

1

まず、Haskellはデータを自分自身に「コピー」することはできません。通常、それはデータが変更可能であることを意味します。 Haskellでは、変数 "variable"を変更することはできません。したがって、与えられた変数が提示する値を変更することはできません。

は、の前のバージョンと同じ名前の新しい変数を定義しています。しかし、これを正しく行うには、の古い変数を参照する必要があります。新しく定義された変数ではありません。だからあなたの本来の定義

let rec = rec { field=1 } 

は、再帰的な定義である、名前recは、それ自体を参照してください。しかし、あなたが意図したのは、早い段階で定義されたrecを参照することです。

これは名前の競合です。

ハスケルは、これを回避するためにいくつかのmachenismを持っています。 1つはあなたの "一時的な名前変更"です。オリジナル例えば

これはこれはあなたの特定のソリューションのように見えます

let rec' = rec 
let rec = rec' { field=1 } 

のように見えます。しかし、これはコマンドライン環境でのみ利用可能であることを覚えておいてください。

let rec = MyModule.rec { field=1 } 
:あなたが関数内でこれを使用しようとした場合、あなたは

let rec' = rec in let rec = rec' { field=1 } in ... 

を記述する必要がありここでrecは、別のモジュール(たとえば「MyModule」)に属している場合に便利かもしれない別の回避策は、あります

2

変数を更新する必要はありません。常に新しい値で別の変数を作成することができます。あなたの場合はlet rec' = rec{field = 1}です。

不必要にパフォーマンスと値が不安になることがありますか?それはあなたのコードではなく、コンパイラの仕事です。コード内に2つの変数を宣言しても、コンパイラはメモリ内に2つの変数を作成し、その場所で更新するだけです。

コンパイル時にコードが複雑になって最適化が失敗することがあります。中間のコア言語、または最終アセンブリを検査することで分かります。どの機能が遅いかを知るための最初のプロファイル:それがちょうど追加のIntまたはDoubleの場合、気にしません。

コンパイラが最適化できなかった機能があり、時間がかかる場合は、書き換えてメモリを自分で処理できます。ボックスのないベクトル、IOとSTモナド、さらには言語拡張機能のようなものを使用して、ネイティブのマシンレベルのタイプにアクセスします。

関連する問題