Difference between revisions of "10分で学ぶHaskell"
Ymotongpoo (talk | contribs) |
Ymotongpoo (talk | contribs) |
||
Line 117: | Line 117: | ||
これらの型は"型クラス"を用いています。つまり: |
これらの型は"型クラス"を用いています。つまり: |
||
− | * <hask>42</hask> |
+ | * <hask>42</hask> はあらゆる数字型として使うことができます。(このおかげで<hask>5</hask>を<hask>Int</hask>としても<hask>Double</hask>としても宣言できたわけです) |
+ | * <hask>42.0</hask> はどのような分数型にもなれますが、整数型にはなれません。 |
||
− | * <hask>42.0</hask> can be any fractional type, but not an integral type. |
||
+ | * <hask>gcd 15 20</hask> (ちなみにこれは関数呼び出しです)はどのような整数型にもなれますが、分数型にはなれません。 |
||
− | * <hask>gcd 15 20</hask> (which is a function call, incidentally) can be any integral type, but not a fractional type. |
||
+ | Haskellの"prelude"(初期状態でimportされるライブラリの一部)内には5種類の数値型があります。 |
||
− | There are five numeric types in the Haskell "prelude" (the part of the library you get without having to import anything): |
||
− | * <hask>Int</hask> |
+ | * <hask>Int</hask> は少なくとも30ビットの精度の整数です |
− | * <hask>Integer</hask> |
+ | * <hask>Integer</hask> は精度無制限の整数型です |
− | * <hask>Float</hask> |
+ | * <hask>Float</hask> は単精度浮動小数点数です |
− | * <hask>Double</hask> |
+ | * <hask>Double</hask> は倍精度の浮動小数点数です |
− | * <hask>Rational</hask> |
+ | * <hask>Rational</hask> は分数型で丸め誤差がありません |
− | + | これら5つはすべて <hask>Num</hask>型クラスの '''インスタンス''' です。最初の2つは<hask>Integral</hask>の '''インスタンス''' で、残り3つは<hask>Fractional</hask>の '''インスタンス''' です。 |
|
+ | ごちゃまぜにしてみましょう。 |
||
− | Putting it all together, |
||
Prelude> <hask>gcd 42 35 :: Int</hask> |
Prelude> <hask>gcd 42 35 :: Int</hask> |
||
Line 140: | Line 140: | ||
No instance for (Integral Double) |
No instance for (Integral Double) |
||
+ | 最後に触れておいたほうがよい型は <hask>()</hask>で表されるもので "Unit" と呼ばれます。この型はたった一つの値をとり、やはり<hask>()</hask>と書かれ "Unit" と呼ばれます。 |
||
− | The final type worth mentioning here is <hask>()</hask>, pronounced "unit." It only has one value, also written as <hask>()</hask> and pronounced "unit." |
||
Prelude> <hask>()</hask> |
Prelude> <hask>()</hask> |
||
Line 147: | Line 147: | ||
<hask>() :: ()</hask> |
<hask>() :: ()</hask> |
||
+ | これはC言語系の言語での<tt>void</tt>キーワードに似ています。もしなにも返り値を期待しない場合、I/Oアクションから<hask>()</hask>を返すことができます。 |
||
− | You can think of this as similar to the <tt>void</tt> keyword in C family languages. You can return <hask>()</hask> from an I/O action if you don't want to return anything. |
||
== 構造化されたデータ == |
== 構造化されたデータ == |
Revision as of 05:22, 4 December 2009
概要
Haskellは関数型で(つまりすべてが関数呼びだしで処理される)、静的な暗黙的型付けで(typeはコンパイラによって確認され、明示的に宣言する必要はない)、遅延評価(必要となるまで処理されない)の言語です。系統が近い言語として最も人気のあるのはおそらくML系の言語でしょう。(MLは遅延評価ではないですが)
最も普及しているHaskellのコンパイラは GHC です。GHCは http://www.haskell.org/ghc/download.html からダウンロードできます。GHCのバイナリは GNU/Linux、 FreeBSD、 MacOS、Windows、Solarisで動作します。GHCをインストールすると、ghcと ghciという2つのプログラムが入っているのが確認できます。最初のghcの方はHaskellのライブラリやアプリケーションをバイナリコードにコンパイルします。 ghciはインタプリタで、Haskellコードを書いてすぐに結果を得ることができる環境です。
簡潔な表現
たいていの数学的表現は直接ghciに入力して結果を得ることができます。Prelude>はGHCiのデフォルトのプロンプトです。
Prelude>3 * 5
15 Prelude>4 ^ 2 - 1
15 Prelude>(1 - 5)^(3 * 2 - 4)
16
文字列は"ダブルクォート(二重引用符)"で囲みます。文字列の結合をするときは++
を使います。
Prelude>"Hello"
"Hello" Prelude>"Hello" ++ ", Haskell"
"Hello, Haskell"
functionを呼び出すときは関数の後に直接引数を並べて行います。関数呼び出しに括弧は必要ありません。こんな感じです:
Prelude>succ 5
6 Prelude>truncate 6.59
6 Prelude>round 6.59
7 Prelude>sqrt 2
1.4142135623730951 Prelude>not (5 < 3)
True Prelude>gcd 21 14
7
コンソール
I/O actionsはコンソールからの入出力を行うのに使います。有名なものの例は:
Prelude>putStrLn "Hello, Haskell"
Hello, Haskell Prelude>putStr "No newline"
No newlinePrelude>print (5 + 4)
9 Prelude>print (1 < 2)
True
putStr
関数 と putStrLn
関数 は文字列をターミナルに出力します。print
関数はどんな型の値でも出力します。(文字列をprint
するときは、引用符が前後につきます)
ひとつの表現の中で複数の I/O アクションを使いたい場合は、do
ブロックを使います。アクションはセミコロンで区切られます。
Prelude>do { putStr "2 + 2 = " ; print (2 + 2) }
2 + 2 = 4 Prelude>do { putStrLn "ABCDE" ; putStrLn "12345" }
ABCDE 12345
読み込みは getLine
(String
を返します)かreadLn
(どんな型の値でも返します)でできます。<-
シンボルはI/Oアクションの結果に名前を束縛するために使います。
Prelude>do { n <- readLn ; print (n^2) }
4 16
(4が入力で16が結果です。)
実際にはdo
ブロックを書くには別の方法があります。Haskellでは中括弧やセミコロンをあまり使わないので、インデントが重要になります。ghciでは書きにくいかもしれないので、ソースファイル(例えばTest.hs)に書いてビルドしてみましょう。
main = do putStrLn "What is 2 + 2?"
x <- readLn
if x == 4
then putStrLn "You're right!"
else putStrLn "You're wrong!"
ビルドは ghc --make Test.hs でできます。実行ファイル Test ができるはずです。(WindowsではTest.exeとなります)ここでif
式も一緒に習得できてしまいましたね。
do
の直後の最初の空白でない文字は特別な文字です。上の例の場合ではputStrLn
の先頭のpがそれです。do
ブロック内のすべての行はそのp
と同じ列から開始します。もしより深くインデントすれば、それ以前の行の一部となります。逆に浅くインデントすれば、do
ブロックはそこで終わります。これは"レイアウト"と呼ばれ、Haskellでは、いつも行の終了文字や中括弧を使うのは面倒なので、代わりにレイアウトを使います。(then
句とelse
句はこれが理由でインデントされなければならないのです。もしif
と同じ列から開始した場合、別の行と解釈されてしまって、エラーとなります。)
(Note: レイアウトを使う場合はインデントにタブを用いないでください。技術的にはタブが8文字であれば動作しますが、おすすめできません。また中にはそういう人もいるかもしれませんが、プロポーショナルフォントを使うこともおすすめしません。
簡潔な型
これまで、type宣言に関して全く触れてきませんでした。その必要がなかったのですが、それはHaskellが型推論を行うからです。一般的に型宣言はしたい場合以外はする必要がありません。型宣言をする場合は::
を使って行います。
Prelude>5 :: Int
5 Prelude>5 :: Double
5.0
HaskellではType (そして後で触れる型class)は常に大文字から開始します。変数は常に小文字から始まります。これは言語仕様であり、命名規則(naming convention)ではありません。
ghciを使って、ある値がどんな型かを確認することもできます。普段、自分では型宣言をしないので、この機能は役に立ちます。
Prelude> :tTrue
True :: Bool
Prelude> :t'X'
'X' :: Char
Prelude> :t"Hello, Haskell"
"Hello, Haskell" :: [Char]
(上の例を見て気づいたように、[Char]
はString
の別名です。あとでをsection on listsを見てみましょう)
数字に関してはもっと面白いものが見られます。
Prelude> :t42
42 :: (Num t) => t
Prelude> :t42.0
42.0 :: (Fractional t) => t
Prelude> :tgcd 15 20
gcd 15 20 :: (Integral t) => t
これらの型は"型クラス"を用いています。つまり:
42
はあらゆる数字型として使うことができます。(このおかげで5
をInt
としてもDouble
としても宣言できたわけです)42.0
はどのような分数型にもなれますが、整数型にはなれません。gcd 15 20
(ちなみにこれは関数呼び出しです)はどのような整数型にもなれますが、分数型にはなれません。
Haskellの"prelude"(初期状態でimportされるライブラリの一部)内には5種類の数値型があります。
Int
は少なくとも30ビットの精度の整数ですInteger
は精度無制限の整数型ですFloat
は単精度浮動小数点数ですDouble
は倍精度の浮動小数点数ですRational
は分数型で丸め誤差がありません
これら5つはすべて Num
型クラスの インスタンス です。最初の2つはIntegral
の インスタンス で、残り3つはFractional
の インスタンス です。
ごちゃまぜにしてみましょう。
Prelude>gcd 42 35 :: Int
7 Prelude>gcd 42 35 :: Double
<interactive>:1:0: No instance for (Integral Double)
最後に触れておいたほうがよい型は ()
で表されるもので "Unit" と呼ばれます。この型はたった一つの値をとり、やはり()
と書かれ "Unit" と呼ばれます。
Prelude>()
()
Prelude> :t()
() :: ()
これはC言語系の言語でのvoidキーワードに似ています。もしなにも返り値を期待しない場合、I/Oアクションから()
を返すことができます。
構造化されたデータ
Basic data types can be easily combined in two ways: lists, which go in [square brackets], and tuples, which go in (parentheses).
Lists are used to hold multiple values of the same type.
Prelude>[1, 2, 3]
[1,2,3] Prelude>[1 .. 5]
[1,2,3,4,5] Prelude>[1, 3 .. 10]
[1,3,5,7,9] Prelude>[True, False, True]
[True,False,True]
Strings are just lists of characters.
Prelude>['H', 'e', 'l', 'l', 'o']
"Hello"
The :
operator appends an item to the beginning of a list. (It is Haskell's version of the cons function in the Lisp family of languages.)
Prelude>'C' : ['H', 'e', 'l', 'l', 'o']
"CHello"
Tuples hold a fixed number of values, which can have different types.
Prelude>(1, True)
(1,True) Prelude>zip [1 .. 5] ['a' .. 'e']
[(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]
The last example used zip
, a library function that turns two lists into a list of tuples.
The types are probably what you'd expect.
Prelude> :t['a' .. 'c']
['a' .. 'c'] :: [Char]
Prelude> :t[('x', True), ('y', False)]
[('x', True), ('y', False)] :: [(Char, Bool)]
Lists are used a lot in Haskell. There are several functions that do nice things with them.
Prelude>[1 .. 5]
[1,2,3,4,5]
Prelude>map (+ 2) [1 .. 5]
[3,4,5,6,7]
Prelude>filter (> 2) [1 .. 5]
[3,4,5]
There are two nice functions on ordered pairs (tuples of two elements):
Prelude>fst (1, 2)
1
Prelude>snd (1, 2)
2
Prelude>map fst [(1, 2), (3, 4), (5, 6)]
[1,3,5]
Also see how to work on lists
Function 定義
We wrote a definition of an IO action earlier, called main
:
main = do putStrLn "What is 2 + 2?"
x <- readLn
if x == 4
then putStrLn "You're right!"
else putStrLn "You're wrong!"
Now, let's supplement it by actually writing a function definition and call it factorial
. I'm also adding a module header, which is good form.
module Main where
factorial n = if n == 0 then 1 else n * factorial (n - 1)
main = do putStrLn "What is 5! ?"
x <- readLn
if x == factorial 5
then putStrLn "You're right!"
else putStrLn "You're wrong!"
Build again with ghc --make Test.hs. And,
$ ./Test What is 5! ? 120 You're right!
There's a function. Just like the built-in functions, it can be called as factorial 5
without needing parentheses.
Now ask ghci for the type.
$ ghci Test.hs << GHCi banner >> Ok, modules loaded: Main. Prelude Main> :tfactorial
factorial :: (Num a) => a -> a
Function types are written with the argument type, then ->
, then the result type. (This also has the type class Num
.)
Factorial can be simplified by writing it with case analysis.
factorial 0 = 1
factorial n = n * factorial (n - 1)
便利な構文
A couple extra pieces of syntax are helpful.
secsToWeeks secs = let perMinute = 60
perHour = 60 * perMinute
perDay = 24 * perHour
perWeek = 7 * perDay
in secs / perWeek
The let
expression defines temporary names. (This is using layout again. You could use {braces}, and separate the names with semicolons, if you prefer.)
classify age = case age of 0 -> "newborn"
1 -> "infant"
2 -> "toddler"
_ -> "senior citizen"
The case
expression does a multi-way branch. The special label _
means "anything else".
ライブラリを使う
Everything used so far in this tutorial is part of the Prelude, which is the set of Haskell functions that are always there in any program.
The best road from here to becoming a very productive Haskell programmer (aside from practice!) is becoming familiar with other libraries that do the things you need. Documentation on the standard libraries is at http://haskell.org/ghc/docs/latest/html/libraries/. There are modules there with:
- Useful data structures
- Concurrent and parallel programming
- Graphics and GUI libraries
- Networking, POSIX, and other system-level stuff
- Two test frameworks, QuickCheck and HUnit
- Regular expressions and predictive parsers
- More...
module Main where
import qualified Data.Map as M
errorsPerLine = M.fromList
[ ("Chris", 472), ("Don", 100), ("Simon", -5) ]
main = do putStrLn "Who are you?"
name <- getLine
case M.lookup name errorsPerLine of
Nothing -> putStrLn "I don't know you"
Just n -> do putStr "Errors per line: "
print n
The import
says to use code from Data.Map
and that it will be prefixed by M
. (That's necessary because some of the functions have the same names as functions from the prelude. Most libraries don't need the as
part.)
If you want something that's not in the standard library, try looking at http://hackage.haskell.org/packages/hackage.html or this wiki's applications and libraries page. This is a collection of many different libraries written by a lot of people for Haskell. Once you've got a library, extract it and switch into that directory and do this:
runhaskell Setup configure runhaskell Setup build runhaskell Setup install
On a UNIX system, you may need to be root for that last part.
10分以上かかるので省いたトピック
- Advanced data types
- Arithmetic lists
- List comprehensions
- Type synonyms
- data vs newtype (and here)
- Type classes and instances
- Advanced syntax
- Advanced functions
- Monads
- File I/O
- Reading files
- Writing Files