Haskell Questチュートリアル-先

家の西

あなたはホワイトボードの西にあるオープンフィールドに立ち、正面玄関に乗り込みました。

ここには小さなメールボックスがあります。



>メールボックスを開く

小さなメールボックスを開くと、リーフレットが表示されます。



>リーフレットを読む

(テイケン)

ZORKへようこそ!



ZORKは、冒険、危険、およびcなゲームです。 その中で、あなたは人間によってこれまでで最も驚くべき領域のいくつかを探検します。 コンピュータが1つもなければなりません!」





内容:

あいさつ

パート1-先

パート2-フォレスト

パート3-ポリアナ

パート4-キャニオンのビュー

パート5-ホール



パート1

Haskell言語のすべての基本を知ることはできず、クエストに役立つ1つの関数を書くこともできません。



だから、あなたは一番最初に、閉じたドアの前に立って、あなたは郵便受けを見ます。



Haskellはプログラミングに多くを必要としません。 まず第一に、欲望。 インタプリタとコンパイラも役に立つかもしれません。 GHCコンパイラ(Glasgow Haskell Compiller)をHaskell Platform for Windowsの一部として使用しますが、それは単に便利だからです。 (Linuxを使用している場合、アドバイスは必要ありません。すべてを機能させる方法は私よりもよく知っています。)インストールは簡単です。 ダウンロードしてインストールしてください。 インストール後、ghciコマンドを実行します。 見つからない場合は、PATHにフォルダー "... \ Haskell Platform \ xyz0 \ bin \"を追加します。 Haskellコードは「* .hs」ファイルに保存されます。 HugIDEまたはメモ帳++(私と同じように)で作成できます。 Haskellプラットフォームをインストールすると、* .hsファイルがGHCiインタープリターに関連付けられます。これは、後で見るように非常に便利です。 Hackageのすべての長所の巨大なリポジトリをダウンロードすることもできます。そこには、簡単なゲームadvgameがあります。 彼女は、自分自身を書く上で私にとってタイプと基準点でした。



クエスト用のフォルダーを作成します。 空のQuestMain.hsファイルを作成して実行します。 GHCiインタープリターのコンソールが表示されます。これはデバッグに役立ちます。 GHCiがいくつかのライブラリをロードし、1ファイルのうち1フ​​ァイルを正常にコンパイルし、「OK、ロードされたモジュール:Main」と表示されます。コマンドプロンプトで「* Main>」と入力します。 いいえ、いいえ、プログラムを作成しません。 次に、このツールを少し調べて、後でプログラムのデバッグが簡単になるようにします。



*メイン> 4

4

*メイン> 2 + 4-7

-1

*メイン> 9 / 2 * 5.0

0.9

*メイン> - 1 + 4

3

*メイン> 7 == 7

本当

*メイン> -5 > 0





数学関数も使用できます:sin x、cos x、tan x、atan x、abs x。



*メイン> sin 10 * sin 10 + cos 10 * cos 10

1.0

*メイン> sin 2 * cos 10 + tan 5

-4.374758876543559




Haskellは大文字と小文字を区別する言語です(C ++、Javaなど)。 あなたは常にcOS機能を望んでいたことを知っていますが、そうではありません。 「cos」のみがあります。



*メイン> cos cos cos 0.5

0.8026751006823349

*メイン> cos pi

-1.0




これはすべて予想され、理解できることです。 piは、数piを返す関数であり、引数はありません。 三角関数は1つの引数を受け取ります。 複数の引数を持つ関数はどうですか? 構文は単純です:最初に関数の名前、次に引数。 角かっこ、コンマ、またはセミコロンなし。



*メイン> logBase 2 2

1.0

*メイン> logBase sin 2 2

-7.28991425837762




2番目の例では、sin 2をかっこで囲み、これがlogBase関数の#1引数になるようにすることが重要です。 これが行われない場合、インタプリタは2つの代わりに3つの引数(sin、2、2)をlogBase関数に渡したと見なし、誓います:



*メイン> logBase sin 2 2



<インタラクティブ>11

Floating a0- > a0 )の インスタンスはありません

' logBase 'の使用起因する

考えられる修正: Floating a0- > a0 )の インスタンス宣言を追加します

式内: logBase sin 2 2

「it」の方程式:it = logBase sin 2 2

..................




彼が私たちに言ったことは、私たちはまだそれについて考えません。 これは王室の問題ではなく、もっと重要な問題があります。



数学的な機能を含むその他の機能は、Haskellプラットフォームにある付属ドキュメント(「GHCライブラリドキュメント」)にあります。 デフォルトでは、Preludeモジュールにあるすべての機能が使用可能です。 このモジュールをロードしなかった、それ自体をロードしました。 罪、コス、その他が定義されています。 Preludeには便利な機能が多数含まれています。これらは最も頻繁に使用されるため、まとめられています。 Preludeのドキュメントをチェックすると、次のような異常な、奇妙な構成が表示されます。



単語 :: 文字列 -> [ 文字列 ]




またはこれも:



Eq a => Eq たぶん a




分かりませんか? 何も、私たちはそれを理解しません。





何かがありますが、少しだけ行を作ってみましょう。 Haskellの文字列は、二重引用符内のC:文字と同じように見えます。 特殊文字\ n(「改行」)も機能します。



*メイン> 「こんにちは、世界!」

「Hello world!」

*メイン> 「こんにちは、 \ nワールド!」

「こんにちは、 \ n世界!」




うまくいかなかったのは?? A. ghciで行を書くとき、単純にそれを繰り返します。 同様に、単一の数字またはTrueを繰り返します。



*メイン> 1,000,000

1,000,000

*メイン> True

本当




これはghciデバッグ出力です。これを呼び出しましょう。 行も番号も実際のコンソールにまだ送信されていません。 実際のコンソールで印刷する行を送信しましょう:



* Main > putStrLn "こんにちは、 \ nワールド!"

こんにちは

世界




さて、putStrLn関数は文字列を受け入れ、実際のコンソールに出力しました。 ghciは結果を再現しました。 書き込み:putStrLn、文字列を受け入れ、画面に表示します。 「++」操作で接続することにより、2行を出力できます。



* Main > putStrLn "Hello、world!" ++ " \ nここに行きます!"

ハローワールド

行くぞ!




ghciが正しくなるようにブラケットが必要です。 括弧がなければ、彼は文字通り次のことを考えるでしょう:

putStrLn "こんにちは、世界!" ++ "\ nここに行きます!" <=>(putStrLn "Hello、world!")++( "\ nHere we go!")

コンソールにstring1 add string2を追加しようとしているため、これは待ち伏せです。 出力に文字列を追加するにはどうすればよいですか? 不正行為の内容は次のとおりです。



* Main > putStrLn "Hello、world!" ++ " \ nここに行きます!"



<インタラクティブ>11

期待されるタイプ [ a0 ]を実際のタイプ ' IO 'と一致させることができませんでした

' putStrLn 'の呼び出しの戻り

最初の引数 ' ++ ' では、つまり

' putStrLn “ Hello、world!” '

式内: putStrLn "Hello、world!" ++ " \ nここに行きます!"




コンソールへの2つの出力も加算されません。 さらにいくつかの誤ったオプション:



* Main > putStrLn "Hello、world!" ++ putStrLn " \ n どうぞ !"

* Main > putStrLn "Hello、world!" PutStrLn " \ nここに行きます!"

* Main > "Hello、world!" ++ putStrLn " \ n どうぞ




putStrLn関数がまったくない場合のオプションは機能しますが、「実際」ではなく、文字列のデバッグ出力を取得します。 「\ nHere we go!」という部分が必要でした。 次のようにではなく、新しい行から印刷されました。



* Main > "Hello、world!" ++ " \ nここに行きます!"

「こんにちは! \ nここに行きます!」




誤ったオプションのそれぞれを完了しようとすることで、ghciがあなたをどう思うかを知ることができます。 私たちは皆、これらのインタープリターとコンパイラーのペティネスを知っています!..それらが私たちの世界に住んでいないかのように、正確で正確なものだけを与えてください。



まあ、実際にそうです。 Haskellには独自の世界と独自の法律があります。 法律では、式のタイプが正しいことが必要です。 Haskellは非常に強く型付けされた言語です。 関数、パラメーター、式-すべてに特定のタイプがあります。 異なるタイプのパラメーターが必要な場合、パラメーターを関数に渡すことはできません。 実際のコンソールで数字を入力してみてください。



*メイン> putStrLn 5



<インタラクティブ>110

Num String )の インスタンスはありません

リテラル「 5 」から生じる

考えられる修正: Num String )の インスタンス宣言を追加します

' putStrLn 'の最初の引数つまり ' 5 '

式内: putStrLn 5

「it」の方程式:it = putStrLn 5




通訳者が何かについておなじみの宣誓をしているのがわかります。 putStrLn関数は、文字列(String型)のみを必要としますが、数値を受け取ります。 タイプが一致しません。=>競合があります。 これを行うことができます:



* Main > putStrLn show 5

5




show関数は、可能であれば、引数を文字列に変換し、putStrLnを使用して出力します。 関数(show 5)を実行すると、文字列「5」が取得されるように、次のように入力します。



* Main > putStrLn "String and" ++ show 5 ++ " \ n-再び文字列。"

ストリング 5

-再び文字列




show関数は、多くのタイプを文字列に変換できます。 彼女はクエストで非常に役立ちます。



* Main > putStrLn "sin ^ 2 5 + cos ^ 2 5 =" ++ show sin 5 * sin 5 + cos 5 * cos 5

sin ^ 2 5 + cos ^ 2 5 = 0.9999999999999999




もちろん、putStrLnも同様に便利です。 理論的には、PascalプロシージャwriteLnも何も返さないため、何も返すべきではありません。 しかし、Haskellでは、関数は常に何かを返します。 これを確認してください? ghciでいくつかのユーティリティコマンドを入力できます。「:t」(「:type」)コマンドは、式のタイプを表示します。



*メイン> :t 3 == 3

3 == 3 :: ブール

*メイン> :t 3 / = 3

3 / = 3 :: ブール

* Main > :「f」と入力します

'f' :: Char

* Main >「I am a string」と入力します

「私は文字列です」 :: [ Char ]




ここで、式は特別な種類の関数と見なすことができ、おそらくパラメーターを受け取らず、必然的に結果を返します。 putStrLn関数タイプは次のようになります。



*メイン> :t putStrLn

putStrLn :: 文字列 -> IO




この形式の記述(数学、他の関数型言語)に出会っていない場合は、おそらく私にとってはかつてなかったように、少し珍しいでしょう。 しかし、すぐに良いものに慣れ、Haskellの型も同じように優れています。 ほとんどの場合、それらを指定する必要はありません。Haskellはそれらを自分で削除し、どこかで一致しないものがあれば指で脅しさえします。 私たちはまだ基本的なタイプからすべての種類のデザインを見ています、そして、あなたはそれがどれほど便利であるかを感じるでしょう。 すべての変数が必要な一部のC ++とは異なり、各要素が記述され、ペイントされ、登録されています...



この場合、putStrLnはStringを取り、IO()を返します。 コロンは、関数の名前とそのタイプを区切ります。 「putStrLnにはStringからIO()までの型があります」と読むのが正しいでしょう。 文字列は、入力文字列のタイプです。 「IO()」と入力します-入出力の不可欠な部分(入力/出力、あなたが推測した)。 タイプ「IO()」は、putStrLn関数がプログラムの「クリーンでない」領域にふけることを示します。 このエリアでは、災害でも何でも起こり得ます。私たちはそれについて慎重に考えなければなりません。 しかし、今のところ、私たちは何も考える必要がなく、IO()に迷惑をかけさせません。 大惨事が発生しない関数、副作用が発生しない関数を記述しようとします。



これらは、純粋な決定論と呼ばれます。このような関数は、同じ引数に対して同じ値を返す必要があります。 Haskell言語は、まさにこの概念のために純粋な関数型言語です。 もちろん、ここでは、異なる呼び出しで異なる結果を得ることができる関数(たとえば、疑似乱数ジェネレーター)をどう処理するかという疑問が生じます。 そして、データを変更する方法は? メモリを操作する方法は? キーボード入力の読み方 結局のところ、これはすべて非決定論につながります。 Haskellには、これらの問題やその他の問題を適切に解決するための特別なメカニズム(「モナド」)があります。 今後、この会話に戻ります。




そのため、テキストエディターQuestMain.hsで開きます。 空がありますが、これはほんの始まり、しきい値です。 すぐに人魚がここで泳ぎ、ドラゴンが飛びます。 それまでの間、2つの数値の積を計算する単純な関数を作成します。



prod x y = x * y




割り当てを忘れてください! Haskellには課題はありません! 上にあるのは関数宣言です。 「等しい」とは、prodが2つの引数xとyで機能することを意味し、式x * yと一致します。 この式は、関数を呼び出すと評価されます。 やってみましょう。 QuestMain.hsファイルを保存します。 すでにghciコンソールを閉じている場合は、再度実行します(ghci QuestMain.hs)。 コンソールが開いている場合は、次のコマンドを入力します。r-これにより、ghciが現在のファイル、つまりQuestMain.hsを強制的に再起動してコンパイルします。



*メイン> :r

[ 1 of 1 ] Mainのコンパイル H:\ Haskell \ QuestTutorial \ Quest \ QuestMain。Hs 解釈済み

OK ロードされたモジュール:Main

*メイン>製品 3 5

15




うまくいく! (動作しない場合は、大文字と小文字の区別、QuestMain.hsが保存されているかどうか、このバージョンがghciにロードされているかどうかを確認します。)数字3と5はそれぞれ変数xとyに関連付けられています。 インタープリターはprod 3 5を式3 * 5で置き換えます。これは計算されます。



*メイン>製品 3 2 + 3

15

*メイン>製品 3 cos pi

-3.0




さらにいくつかの関数を作成してテストします。 (以下、ファイルに関数を記述することは指定しなくなりましたが、ghciでテストします。)たとえば、次のようになります。



printString str = putStrLn str

printSqrt x = putStrLn " Sqrt of" ++ show x ++ "=" ++ show sqrt x



*メイン> printString "dfdf"

dfdf

*メイン> printSqrt 4

Sqrt 4.0 = 2.0

*メイン> printSqrt -4

平方根-4.0 = NaN




後者の場合、負の数の平方根は「Not a Number」という結果になります。 この結論が私たちに合わず、文字列「x <0!」を負のxに出力したいとします。 printSqrt関数をいくつかの方法で書き直し、同時にいくつかの非常に有用な構成要素を調べます。



printSqrt1 x =

x < 0の 場合

次に putStrLn "x <0!"

else putStrLn " Sqrt of" ++ show x ++ "=" ++ show sqrt x



printSqrt2 x = case x < 0 of

True- > putStrLn "x <0!"

False- > putStrLn " Sqrt of" ++ show x ++ "=" ++ show sqrt x



*メイン> printSqrt1 -4

x < 0

*メイン> printSqrt2 -4

x < 0




等号の後にあるものはすべて、すべてのオプション(代替)を考慮に入れるべき式であるため、他になければifはできません。 いくつかのオプションを考慮しなかったが失敗した場合は、選択肢が不完全であるというエラーが表示されます。



printSqrt2 x = case x < 0 of

True- > putStrLn "x <0!"



*メイン> :r

...

*メイン> printSqrt2 -4

x < 0

*メイン> printSqrt2 10

***例外:H:\ Haskell \ QuestTutorial \ Quest \ QuestMain hs: 12、16 - 13、41 ケースの非網羅的なパターン




また、ケース構文では、インデント(「インデント」)が重要であることに注意してください。 それらは同じでなければなりません-これはそこのスペースまたはタブにも適用されます。 コンパイルしてみてください:



printSqrt2 x = case x < 0 of

True- > putStrLn "x <0!"

False- > putStrLn " Sqrt of" ++ show x ++ "=" ++ show sqrt x



*メイン> :r

[ 1 of 1 ] Mainのコンパイル H:\ Haskell \ QuestTutorial \ Quest \ QuestMain。Hs 解釈済み



H:\ Haskell \ QuestTutorial \ Quest \ QuestMain hs: 1423

入力でのエラーが発生しました ' -> '

失敗しモジュールがロードされました:なし




ケースのデザインはとても便利です。 if-then-elseよりも読みやすく、拡張しやすいです。 もちろん、ネストされた一連のif-then-elseで表現できるため、構造は同等です。 秘密を教えてください。ケースはさらに機能的であり、すぐにわかります。 それまでの間、追加の例。 少し人工的ですが、それは問題ではありません:



thinkAboutSquaredX x = ケース x

0.0 -> 「0 * 0 = 0であるため、xは0だと思います。」

1.0- > "1 * 1 = 1であるため、xは1です。"

4.0 -> 「2 x 2 = 4なので、xは2です。」

9.0- > "x = 3."

16.0- > 「まさか、x = 4」

25.0 -> 「Ha!X = 5!」

それ以外の場合 -> x < 0の 場合、 「x <0!」 else "Sqrt" ++ show x ++ "=" ++ show sqrt x






*メイン> thinkAboutSquaredX 1

「1 * 1 = 1であるため、Xは1です。」

*メイン> thinkAboutSquaredX 25

「ハ! x = 5!」



それ以外の場合の単語は 「そうでなければ」も意味します。 残りのオプションが順番に近づいていない場合そうでない場合に適しています。これはTrueの同義語にすぎないためです。 下部にあるオプションはすべて使用できなくなるため、中央に挿入しないでください。



thinkAboutSquaredX x = ケース x

0.0 -> 「0 * 0 = 0であるため、xは0だと思います。」

1.0- > "1 * 1 = 1であるため、xは1です。"

そうでなければ -> if x < 0 then “ x <0!” else “ Sqrt” ++ show x ++ "=" ++ show sqrt x

4.0 -> 「2 x 2 = 4なので、xは2です。」

9.0- > "x = 3."

16.0- > 「まさか、x = 4」

25.0- > 「ハ! x = 5!」




コードはコンパイルされますが、一致するサンプルの共通部分に関する警告が表示されます。



*メイン> :r

[ 1 of 1 ] Mainのコンパイル H:\ Haskell \ QuestTutorial \ Quest \ QuestMain。Hs 解釈済み



H:\ Haskell \ QuestTutorial \ Quest \ QuestMain hs: 1624

警告:パターンマッチ es が重複しています

代替ケースの場合

4.0- > ...

9.0- > ...

16.0- > ...

25.0- > ...

OK ロードされたモジュール:Main

*メイン> thinkAboutSquaredX 1

「1 * 1 = 1であるため、Xは1です。」

*メイン> thinkAboutSquaredX 9

「Sqrt 9.0 = 3.0」

* Main >それ以外の場合は t

そうでなければ :: Bool

*メイン> それ以外の場合 == True

本当




ここでやっていることを思い出してください:クエストを書いています! すべてのクエストには、場所(移動パスによる)、オブジェクト、プレイヤーアクションの3つのことがあります。 番号がある場合、どのようにして場所の説明を取得できますか? 解決策は次のとおりです。



describeLocation locNumber = ケース locNumber

1- > 「木製のテーブルの真ん中の部屋に立っています。」

2- > 「あなたは小さな木製のフェンスの後ろのナイトガーデンの前に立っています。」

-ここに他の場所の説明を挿入します。

それ以外の場合 -> "不明な場所。"



* Main > describeLocation 2

「あなたは小さな木製のフェンスの後ろのナイトガーデンの前に立っています。」

* Main > describeLocation 444

「場所が不明です。」




注意してください:コメント! 単一行コメントは、二重のマイナス記号で始まります(SQL!のように)。 複数行の文字の場合、{-および-}が使用されます。



describeLocation locNumber = ケース locNumber

1- > 「木製のテーブルの真ん中の部屋に立っています。」

2- > 「あなたは小さな木製のフェンスの後ろのナイトガーデンの前に立っています。」

{-ここに他の場所の説明を挿入します。

またはここ。

adfsdf少数のfef jel jle jkjlefjaiejeo-}

それ以外の場合 -> "不明な場所。"




よくここに。 基本的なHaskellコンストラクトのすべてを知ることはできませんでした。 putStrLn、show、case-construction、lines、ghci-これらはすべて将来的に必要になります。 クエスト用の関数も1つ作成しました。 おそらく十分です。 第二部では、クエストの作業を開始し、その過程で他のすばらしいHaskellトリックを学びます。 冒険が待っています!



統合するには、次のタスクを解決できます。



1.関数zを定義し、いくつかの値について計算します。

t =(7 * x ^ 3-ln(abs(a)))/(2.7 * b)

y = sin(t)-sin(a)

z = 8.87 * y ^ 3 + arctan(t)

ここで、x、a、bはFloat型の変数です。



2.関数yを定義し、いくつかの値について計算します。

| ln(abs(sin(x)))x> 5の場合

y = | x <= 5かつa <= 3の場合、x ^ 2 + a ^ 2

| x / a + 7.8 * a x <= 5およびa> 3の場合

ここで、x、aはFloat型の変数です。




目次、リファレンスのリスト、および追加情報は、グリーティングにあります。



All Articles