Hatena::Grouphaskell

lnzntのHaskell日記 このページをアンテナに追加 RSSフィード

2014年11月23日(日)

部分適用

| 10:04 |  部分適用 - lnzntのHaskell日記 を含むブックマーク はてなブックマーク -  部分適用 - lnzntのHaskell日記  部分適用 - lnzntのHaskell日記 のブックマークコメント

部分適用

add x y = x + y
add1    = add 1

実行してみる。

add1 2  -- => 3 になる
add1 10 -- => 11 になる

別の書き方。(部分適用でないのもある(?))

add1 = (\x y -> x + y) 1
add1 = \x -> 1 + x   -- これは部分適用でない(?)
add1 = (+) 1

関数の書き方

| 09:25 |  関数の書き方 - lnzntのHaskell日記 を含むブックマーク はてなブックマーク -  関数の書き方 - lnzntのHaskell日記  関数の書き方 - lnzntのHaskell日記 のブックマークコメント

参考書籍の練習問題の一部である swapa。'A' を 'a' に 'a' を 'A' に書き換える関数。

パターンマッチで。

swapa :: Char -> Char

swapa 'a' = 'A'
swapa 'A' = 'a'
swapa c   = c

ガードで。

swapa :: Char -> Char

swapa c
    | c == 'a'  = 'A'
    | c == 'A'  = 'a'
    | otherwise = c

case を使って。

swapa :: Char -> Char

swapa c = case c of
          'a' -> 'A'
          'A' -> 'a'
          _   -> c

レイアウト規則(オフサイド規則)で少しハマった。

インデントはスペースを使ったほうがいいみたい。vi では「:se et」すべし。


1行で書いでみる。

swapa :: Char -> Char
swapa c = case c of { 'a' -> 'A' ; 'A' -> 'a' ; _ -> c }

ラムダで書いてみる。

swapa = \c -> case c of { 'a' -> 'A' ; 'A' -> 'a' ; _ -> c }

コマンドライン引数を表示するプログラム

| 06:07 |  コマンドライン引数を表示するプログラム - lnzntのHaskell日記 を含むブックマーク はてなブックマーク -  コマンドライン引数を表示するプログラム - lnzntのHaskell日記  コマンドライン引数を表示するプログラム - lnzntのHaskell日記 のブックマークコメント

久々に Haskell の勉強。備忘録。

import System.Environment (getArgs)

main = print =<< getArgs

参考にした書籍では System.getArgs になっていたが、現在は System.Environment.getArgs のようだ。

上のプログラムは以下と等価。

import System.Environment (getArgs)

main = do cs <- getArgs
          print cs

getArgs はモジュール system.Environment に属するアクション

Haskell の関数は参照透明性を持ち副作用がないが、アクションは副作用を扱う。

アクションの結果を取り出し変数(上ではcs)を束縛するのが「<-」。do の中でしか使えないらしい。

アクションの結果を一時変数なしに直接参照(?)するのが「=<<」のようだ。

モジュール

モジュールは「import」によりインポートして使う。

インポートしたモジュールでエクスポートされている関数やアクションなどが使える。

特定のアクション等のみをインポートする場合は上の「(getArgs)」のように指定する。

標準入力からの入力をそのまま出力するプログラムは以下。(cat もどき)

import System.IO (getContents)

main = putStr =<< getContents

「>>=」を使って書き換えると以下。(「>>=」は bind と呼ばれる)

main = getContents >>= putStr

tac もどき

Linuxtac は入力を行単位の逆順に出力するコマンドである。(多分 cat の反対なので tac)

tac もどきのプログラムは以下のようになる。

import System.IO (getContents)

main = putStr . unlines . reverse . lines =<< getContents

「.」は関数を合成する。

Ruby で以下のように書かれているのとだいだい同じ。

puts $<.readlines.reverse.join

関数合成Ruby のメッソドチェーンと対比して考えると、自分的には、理解しやすい。

rev もどき

Linux の rev は入力の各行を逆に並びかえたものを出力するコマンドである。

rev もどきのプログラムは以下のようになる。

import System.IO (getContents)

main = putStr . unlines . map reverse . lines =<< getContents

「.」での関数合成は、引数を一つとる関数しか合成できないらしい。

「map reverse」は部分適用によって引数を一つとる関数に変換されている(と思う)。

Ruby で書くとこうなる。

puts $<.readlines.map(&:reverse).join

Ruby 版は最初の行の改行文字も出力されるので、出力が若干異なる。(ま、いいか)