回答:
それが私に提案された方法、そして今1か月間Haskellでの学習に取り組んだ後に私が本当だと思うのは、関数型プログラミングが興味深い方法であなたの脳をねじるという事実です。 :ループの代わりに、マップ、フォールド、フィルターなどを検討します。一般に、問題について複数の視点がある場合、この問題について推論し、必要に応じて視点を切り替えることができます。
Haskellのもう1つの優れた点は、その型システムです。厳密に型付けされていますが、型推論エンジンは、型に関連する愚かな間違いをしたことを魔法のように教えてくれるPythonプログラムのように感じさせます。この点に関するHaskellのエラーメッセージはいくぶん欠けていますが、言語に慣れるにつれて、自分に言い聞かせるでしょう。これがタイピングの本来の目的です!
これは、 Haskellを学ぶように私を説得した例です(そして少年は私がそうしたことがうれしいです)。
-- program to copy a file --
import System.Environment
main = do
--read command-line arguments
[file1, file2] <- getArgs
--copy file contents
str <- readFile file1
writeFile file2 str
OK、それは短くて読みやすいプログラムです。その意味では、Cプログラムよりも優れています。しかし、これは(たとえば)非常によく似た構造のPythonプログラムとどう違うのでしょうか。
答えは遅延評価です。ほとんどの言語(一部の機能的な言語でも)では、上記のような構造のプログラムでは、ファイル全体がメモリに読み込まれ、新しい名前で再び書き込まれます。
Haskellは「怠惰」です。必要になるまで計算を行わず、ひいては必要のないものを計算しません。たとえば、このwriteFile
行を削除したとしても、Haskellはそもそもファイルから何も読み込まないでしょう。
現状では、HaskellはがにwriteFile
依存していることを認識しているreadFile
ため、このデータパスを最適化できます。
結果はコンパイラに依存しますが、上記のプログラムを実行すると通常は次のようになります。プログラムは最初のファイルのブロック(たとえば8KB)を読み取り、2番目のファイルに書き込み、次に最初のファイルから別のブロックを読み取りますファイル、2番目のファイルなどに書き込みます。(実行strace
してみてください!)
...これは、ファイルコピーの効率的なC実装が行うこととよく似ています。
したがって、Haskellを使用すると、多くの場合パフォーマンスを犠牲にすることなく、コンパクトで読みやすいプログラムを作成できます。
私が追加しなければならないもう1つのことは、Haskellがバグのあるプログラムの作成を単純に難しくしていることです。驚くべき型システム、副作用の欠如、そしてもちろんHaskellコードのコンパクトさは、少なくとも3つの理由でバグを減らします。
プログラム設計の改善。複雑さが軽減されると、論理エラーが減少します。
コンパクトなコード。バグが存在する行が少なくなります。
エラーをコンパイルします。多くのバグが有効なHaskellではありません。
Haskellは万人向けではありません。しかし、誰もがそれを試してみるべきです。
hSetBuffering handle (BlockBuffering (Just bufferSize))
。
Data.Bytestring.Lazy.readFile
。モナドは順序付けられています -これは、大まかに「結果を取り出すときにすべての副作用が行われる」ことを意味します。「怠惰なバイト文字列」の魔法については、これは危険であり、他のほとんどの言語では同様またはより単純な構文で実行できます。
readFile
も同様に遅延IOを行いData.ByteString.Lazy.readFile
ます。したがって、答えは間違っていませんし、コンパイラの最適化だけではありません。実際、これはHaskellの仕様の一部です。「readFile
関数はファイルを読み取り、ファイルの内容を文字列として返します。ファイルは、のようにオンデマンドで遅延して読み取られgetContents
ます。」
const fs = require('fs'); const [file1, file2] = process.argv.slice(2); fs.createReadStream(file1).pipe(fs.createWriteStream(file2))
。バッシュは、あまりにも、似た何かを持っている:cat $1 > $2
あなたは一種の間違った質問をしています。
Haskellはあなたがいくつかのクールな例を見て行くと行く言語ではない「なるほど、私は今見る、だ、それは良いものを作ります!」
それは、私たちがこれらすべての他のプログラミング言語を持っているようなものであり、それらは多かれ少なかれ類似しています。そして、あなたが奇抜さに慣れると完全に異なる方法で完全に異なる奇抜なHaskellがあります。しかし問題は、奇抜さに慣れるのにかなり時間がかかることです。Haskellを他のほぼすべての主流言語と区別するもの:
多くの主流言語とは異なる(しかし一部で共有されている)他のいくつかの側面:
他のいくつかのポスターが答えたように、これらすべての機能の組み合わせは、まったく異なる方法でプログラミングについて考えることを意味します。そして、これをJoe-mainstream-programmerに適切に伝える例(または例のセット)を思いつくのは困難です。それは体験的なものです。(例えとして、1970年の中国旅行の写真を紹介できますが、写真を見ても、当時の中国の生活がどうなっていたかはわかりません。同様に、Haskellを紹介できます。 'クイックソート'ですが、Haskellerとはどういう意味かわかりません。)
実際にHaskellを際立たせているのは、関数型プログラミングを実施するための設計での取り組みです。ほぼすべての言語で関数型のスタイルでプログラミングできますが、最初の都合で放棄するのは非常に簡単です。Haskellでは関数型プログラミングを放棄することはできないため、論理的な結論に到達する必要があります。論理的な結論は、推論しやすい最終的なプログラムであり、最も厄介なタイプのバグのクラス全体を回避します。
実際に使用するためのプログラムを作成する場合、Haskellは実用的な方法で不足しているかもしれませんが、最初の既知のHaskellを使用するためには、最終的なソリューションの方が優れています。私はまだそこにいませんが、これまでのところ、Haskellを学ぶことは言うよりもはるかに啓蒙的でした、Lispは大学にいました。
unsafePerformIO
世界の火傷を見たいだけの人のために;)
大騒ぎの一部は、純粋さと静的型付けにより、積極的な最適化と組み合わせた並列処理が可能になることです。並列言語は今やホットで、マルチコアは少し破壊的です。
Haskellは、高速なネイティブコードコンパイラとともに、ほとんどの汎用言語よりも多くの並列処理オプションを提供します。並列スタイルのこの種のサポートとの競争は本当にありません:
したがって、マルチコアを動作させることに関心がある場合、Haskellは言いたいことがあります。まず、Haskellでの並列プログラミングと並行プログラミングに関する Simon Peyton Jonesのチュートリアルをご覧ください。
ソフトウェアトランザクションメモリは、同時実行性を処理するための非常に優れた方法です。メッセージパッシングよりもはるかに柔軟であり、ミューテックスのようにデッドロックが発生しがちです。 STMのGHCの実装は、最高の1つと考えられています。
私は昨年、Haskellを学び、その中でかなり大きく複雑なプロジェクトを書いてきました。(プロジェクトは自動化されたオプション取引システムであり、取引アルゴリズムから低レベルの高速市場データフィードの解析と処理に至るまですべてがHaskellで行われます。)非常に簡潔で理解しやすい(適切な背景)Javaバージョンよりも、非常に堅牢です。
おそらく、私にとって最大のメリットは、モノイド、モナドなどの制御フローをモジュール化できることでした。非常に簡単な例は、Orderingモノイドです。次のような表現で
c1 `mappend` c2 `mappend` c3
どこc1
とそのリターンにLT
、EQ
またはGT
、c1
返却はEQ
評価し、式が継続する原因となりますc2
。がc2
返されるLT
かGT
、それが全体の値である場合、およびc3
が評価されない場合。この種のことは、モナディックメッセージジェネレーターやパーサーなど、さまざまなタイプの状態を持ち歩いている、さまざまなアボート条件を持っている、またはアボートが本当に意味するかどうか特定の呼び出しを決定できるようにしたい場合に、かなり高度で複雑になります「これ以上処理しない」または「最後にエラーを返すが、さらにエラーメッセージを収集する処理を続行する」という意味です。
これはすべて、習得に時間がかかり、おそらくかなりの努力が必要なものであるため、これらの手法をまだ知らない人にとっては、説得力のある議論をするのは難しい場合があります。All About Monadsチュートリアルは、この1つの側面のかなり印象的なデモンストレーションを提供すると思いますが、この資料に精通していない人が最初または3番目の注意深い読書でさえ「理解する」とは思わないでしょう。
とにかく、Haskellには他にも多くの優れた機能がありますが、これはあまり複雑ではないため、あまり言及されていない主要な機能です。
興味深い例については、http: //en.literateprograms.org/Quicksort_(Haskell)をご覧ください。
興味深いのは、さまざまな言語での実装を見ることです。
Haskellを他の関数型言語と一緒に非常に興味深いものにしているのは、プログラミングの方法について異なる考え方をしなければならないという事実です。たとえば、通常、forループやwhileループは使用しませんが、再帰を使用します。
上記のように、Haskellや他の関数型言語は、並列処理やマルチコアで動作するアプリケーションの作成に優れています。
例をあげることはできませんが、私はOCamlの男ですが、自分のような状況にいるときは、好奇心が効くだけなので、コンパイラー/インタープリターをダウンロードして実行する必要があります。おそらく、特定の関数型言語の長所と短所について、はるかに多くのことを学ぶでしょう。
アルゴリズムや数学の問題を処理するときに私がとてもクールだと思うことの1つは、Haskell固有の計算の遅延評価です。これは、その厳密な機能的性質のためにのみ可能です。
たとえば、すべての素数を計算したい場合は、
primes = sieve [2..]
where sieve (p:xs) = p : sieve [x | x<-xs, x `mod` p /= 0]
結果は実際には無限のリストです。しかし、Haskellはそれを左から右に評価するので、リスト全体を必要とする何かを実行しようとしない限り、プログラムを無限に動かさずに次のように使用できます。
foo = sum $ takeWhile (<100) primes
これは、すべての素数が100未満の合計です。これは、いくつかの理由で便利です。まず、すべての素数を生成する1つの素数関数を作成するだけで、素数を操作する準備がほぼ整います。オブジェクト指向プログラミング言語では、返す前に計算する素数を関数に通知する方法、またはオブジェクトで無限リストの動作をエミュレートする方法が必要です。もう1つは、一般に、計算する対象を表現するコードであり、評価の順序ではなく、コンパイラーが代わりに行うコードです。
これは無限リストに役立つだけでなく、実際には、必要以上に評価する必要がない場合は、いつでも知らなくても使用されます。
いくつかの小さな例を見ることはHaskellを自慢するための最良の方法ではないことを他の人に同意します。しかし、とにかくいくつかあげます。オイラープロジェクトの問題18および67に対する超高速の解決策を次に示します。これらは、三角形の底辺から頂点までの最大合計パスを見つけるように求めます。
bottomUp :: (Ord a, Num a) => [[a]] -> a
bottomUp = head . bu
where bu [bottom] = bottom
bu (row : base) = merge row $ bu base
merge [] [_] = []
merge (x:xs) (y1:y2:ys) = x + max y1 y2 : merge xs (y2:ys)
これは、LeshとMitzenmacherによるBubbleSearchアルゴリズムの完全で再利用可能な実装です。大きなメディアファイルを無駄なくDVDにアーカイブストレージにパックするために使用しました。
data BubbleResult i o = BubbleResult { bestResult :: o
, result :: o
, leftoverRandoms :: [Double]
}
bubbleSearch :: (Ord result) =>
([a] -> result) -> -- greedy search algorithm
Double -> -- probability
[a] -> -- list of items to be searched
[Double] -> -- list of random numbers
[BubbleResult a result] -- monotone list of results
bubbleSearch search p startOrder rs = bubble startOrder rs
where bubble order rs = BubbleResult answer answer rs : walk tries
where answer = search order
tries = perturbations p order rs
walk ((order, rs) : rest) =
if result > answer then bubble order rs
else BubbleResult answer result rs : walk rest
where result = search order
perturbations :: Double -> [a] -> [Double] -> [([a], [Double])]
perturbations p xs rs = xr' : perturbations p xs (snd xr')
where xr' = perturb xs rs
perturb :: [a] -> [Double] -> ([a], [Double])
perturb xs rs = shift_all p [] xs rs
shift_all p new' [] rs = (reverse new', rs)
shift_all p new' old rs = shift_one new' old rs (shift_all p)
where shift_one :: [a] -> [a] -> [Double] -> ([a]->[a]->[Double]->b) -> b
shift_one new' xs rs k = shift new' [] xs rs
where shift new' prev' [x] rs = k (x:new') (reverse prev') rs
shift new' prev' (x:xs) (r:rs)
| r <= p = k (x:new') (prev' `revApp` xs) rs
| otherwise = shift new' (x:prev') xs rs
revApp xs ys = foldl (flip (:)) ys xs
このコードはランダムに意味不明なもののように見えます。しかし、Mitzenmacherのブログエントリを読んでアルゴリズムを理解すると、検索対象について何も言わずにアルゴリズムをコードにパッケージ化できることに驚くことでしょう。
あなたが要求したようにあなたにいくつかの例を与えたので、私は言うでしょう Haskellのを鑑賞するために開始するための最良の方法は:私がDVDパッカー書き込むために必要なアイデアた紙読み取ることがあるのはなぜ関数型プログラミング事項をジョン・ヒューズによってを。この論文は実際にはHaskellよりも古いものですが、Haskellのような人々のアイデアのいくつかを見事に説明しています。
私にとって、Haskellの魅力は、コンパイラが保証する正確さの約束です。それがコードの純粋な部分であっても。
私はたくさんの科学シミュレーションコードを書いており、以前のコードにバグがあり、現在の作業の多くを無効にする可能性があるかどうか、何度も疑問に思っています。
特定のタスクについては、Haskellを使用すると非常に生産的であることがわかりました。
その理由は、簡潔な構文とテストの容易さのためです。
関数宣言の構文は次のようになります。
foo a = a + 5
これが、関数の定義について考えることができる最も簡単な方法です。
逆を書けば
inverseFoo a = a-5
次のように書くことで、ランダム入力の逆であることを確認できます
prop_IsInverse :: Double-> Bool
prop_IsInverse a = a ==(inverseFoo $ foo a)
そしてコマンドラインから呼び出す
jonny @ ubuntu:runhaskell quickCheck + names fooFileName.hs
入力をランダムに100回テストすることにより、ファイル内のすべてのプロパティが保持されていることを確認します。
Haskellがすべてに最適な言語だとは思いませんが、小さな関数の記述やテストに関しては、これ以上良いものはありません。プログラミングに数学的な要素がある場合、これは非常に重要です。
Haskellの型システムに頭を抱えることができれば、それ自体はかなりの成果だと思います。
ループ構造はありません。多くの言語がこの特性を備えていません。
逆説的な見方をすると、Steve YeggeはHindely-Milner言語には優れたシステムを作成するために必要な柔軟性がないと書いています。
HMは、まったく役に立たない正式な数学的な意味で非常にきれいです。いくつかの計算構造を非常にうまく処理します。Haskell、SML、OCamlにあるパターンマッチングディスパッチは特に便利です。当然のことながら、それは他のいくつかの一般的で非常に望ましい構成要素をせいぜいぎこちなく処理しますが、それらは、あなたが誤っている、実際には望んでいないと言って、それらのシナリオを説明しています。ああ、変数を設定するようなものです。
Haskellは学ぶ価値がありますが、独自の弱点があります。