capitalizE

Capitalize ならぬ capitalizE。字面どおり最後の文字だけを大文字に変える。
……いや,名前はともかく(っていうか,こんな動作に名前がついてるとは思えないが)。
toUpper や toLower を使うには Data.Char モジュールを import する。

import Data.Char

capitalizE []     = []
capitalizE [c]    = (toUpper c):[]
capitalizE (c:cs) = (toLower c):(capitalizE cs)

実行結果。

*Main> capitalizE "Haskell"
"haskelL"

OK。

で,Capitalize のほう。こうやったら全部大文字になった。

capitalize []     = []
capitalize (c:cs) = (toUpper c):(capitalize cs)
*Main> capitalize "haskell"
"HASKELL"

”リストの先頭”は常にあるんだから当然だよな。

join

要素と要素の間ごとに新しい要素を挟み込む。

join s []     = []
join s (c:cs) = c:s:(join s cs)

実行結果。

*Main> join '-' "abc"
"a-b-c-"

あれ?

そうか,一番最後の要素も c:cs にマッチングしてるんだな。てことは”最後の要素”を表すパターンがあればいいのか。これでどうだ。

join s []     = []
join s (c:[]) = c:[]
join s (c:cs) = c:s:(join s cs)

実行。

*Main> join '-' "abc"
"a-b-c"

OK。

関数の定義とパターンマッチング

「入門Haskell」から文字列の文字数を数える関数。

bytesCount []     = 0
bytesCount (c:cs) = 1 + bytesCount cs

関数名 bytesCount につづく引数の部分がパターンを表していて,受け取った引数のパターンによって関数本体の内容が変わる。
1行目の [] は空のリスト,c:cs は先頭の要素とそれ以降の要素に分けられることを表している。だからこの関数を評価すると,引数が空リストなら 0,空でないなら(先頭とそれ以降に分けられるから)先頭の文字数 1 に以降の文字数を足したもの,となる。
パターンがいくつもある場合には定義をいくつも書けばいい。パターンは上から順にチェックされる。

リストの操作は基本的に先頭から順に,再帰的にすればいいようだ。

ちょっと練習。すべての要素を足し合わせる。

mySum []     = 0
mySum (c:cs) = c + mySum cs

実行結果。

*Main> mySum [1,2,3,4,5,6,7,8,9,10]
55
*Main> mySum []
0

href

via. http://d.hatena.ne.jp/nskj77/20060401

コマンドラインから使えるリファレンス。Refe の Haskell 版。バージョンは 0.3.1。

http://i.loveruby.net/w/href.html

インストールは README.jp に書いてあるとおりにすれば簡単……とはいかない。Windows だから。
仕方がないので Makefile をながめて次のように書き換えた。換えたところだけ示す。

bindir = c:\usr\local\bin
EXEEXT = .exe
# mkdir -p $(bindir)

1行目はコマンドのインストール先,c:\usr\local\bin にインストールすることにした。
2行目は実行ファイルの拡張子。3行目は今回はすでにフォルダがあるので全体をコメントアウトした。もしないなら -p オプションだけ削除する。
これで良し。make はないので nmake を使う。

>nmake
Microsoft (R) Program Maintenance Utility   Version 1.50
Copyright (c) Microsoft Corp 1988-94. All rights reserved.
ghc -cpp --make -package network href.hs -o href.exe
Chasing modules from: href.hs
(以下略)
>nmake install
Microsoft (R) Program Maintenance Utility   Version 1.50
Copyright (c) Microsoft Corp 1988-94. All rights reserved.
ghc -cpp --make -package network href.hs -o href.exe
Chasing modules from: href.hs
(以下略)

次,データベースの準備。
c:\usr\share\href にインストールするので c:\usr\share を先に作っておく。href はなくてもいい。

>set HREF_DATADIR=c:\usr\share\href
>mkhref ref/*.*

これでOK。あとはコントロールパネルで環境変数 HREF_DATADIR を設定しておけば普通に使える。

>href filter
Data.List.filter
filter :: (a -> Bool) -> [a] -> [a]    PRELUDE
filter f xs
・・ケ・ネ xs 、ホ、ヲ、チ f 、ャ True 、ヒ、ハ、・ラチヌ、タ、ア、・
スク、皃ソ・・ケ・ネ、ヨ、ケ。」

って,文字化けしてる!……EUCかぁ。

追記:
ref 以下のファイルを ShiftJIS に変換してからインストールしたら直った。
変換に使った Ruby のスクリプト。

require 'find'
require 'fileutils'
require 'nkf'

src = ARGV.shift
dest = ARGV.shift

Find.find(src) do |f|
  case File.ftype(f)
  when "directory"
    FileUtils.mkdir_p(f.sub(src, dest))
  when "file"
    File.open(f, "r") do |infile|
      File.open(f.sub(src,dest), "w") do |outfile|
        outfile.write NKF.nkf("-E -s", infile.read)
      end
    end
  end
end

さらに追記:
むぅ,こうすればコマンド2つですんだ。

>mkdir ref.sjis
>for %X in (ref/*) do nkf -E -s ref/%X > ref.sjis/%X

リストを操作する関数(その2)

zip は2つのリストからそれぞれの要素を取り出して,タプル(組)にしたリストを返す。

Prelude> zip [1,2,3] "abc"
[(1,'a'),(2,'b'),(3,'c')]

タプルには違う型を含めることができる。
zipWith はタプルを作る代わりに関数を適用する。

Prelude> zipWith (+) [1,2,3] [10,20,30]
[11,22,33]

上の例では + を zipWith の第1引数と渡しているけど,()で囲んでやらないとエラーになる。
うまく zipWith の引数として認識できないってことかな。

Prelude> zipWith + [1,2,3] [10,20,30]
<interactive>:1:10:
Couldn't match `[a]' against `t -> t1'
Expected type: [a]
Inferred type: t -> t1
Probable cause: `[1, 2, 3]' is applied to too many arguments in the call
([1, 2, 3] [10, 20, 30])
In the second argument of `(+)', namely `[1, 2, 3] [10, 20, 30]'

ところでタプルって何に使うんだろ。

追記: zipWith の引数について
こうすると同じメッセージがでる。つまり二つのリストが zipWith ではなく + の引数だと解釈されてしまうってことか。

Prelude> zipWith (+ [1,2,3] [10,20,30])
<interactive>:1:11:
Couldn't match `[a]' against `t -> t1'
Expected type: [a]
Inferred type: t -> t1
Probable cause: `[1, 2, 3]' is applied to too many arguments in the call
([1, 2, 3] [10, 20, 30])
In the second argument of `(+)', namely `[1, 2, 3] [10, 20, 30]'

リストと値の型

ひとつのリスト中に違う型の値を含めることはできない。

Prelude> [1,2,'a']
<interactive>:1:1:
    No instance for (Num Char)
      arising from the literal `1' at <interactive>:1:1
    Probable fix: add an instance declaration for (Num Char)
    In the list element: 1
    In the definition of `it': it = [1, 2, 'a']

当然連結もダメ。

Prelude> [1,2,3] ++ ['a','b','c']
<interactive>:1:1:
    No instance for (Num Char)
      arising from the literal `1' at <interactive>:1:1
    Probable fix: add an instance declaration for (Num Char)
    In the list element: 1
    In the first argument of `(++)', namely `[1, 2, 3]'
    In the definition of `it': it = [1, 2, 3] ++ ['a', 'b', 'c']

リストを操作する関数

head はリストの先頭の要素,tail は先頭以外の要素を返す。

Prelude> head [1,2,3,4,5]
1
Prelude> tail [1,2,3,4,5]
[2,3,4,5]

最後の要素を得るには last が使える。

Prelude> last "abc"
'c'

文字列にも使える。

Prelude> head "abc"
'a'

演算子も関数。 !! でインデックスに対応する要素を返し,++ でリストを連結。

Prelude> "abcde" !! 3
'd'
Prelude> "abc" ++ "ef"
"abcef"

文字と文字列、リスト

‘(シングルクォート)’でかこむ文字はChar型。

Prelude> :type 'a'
'a' :: Char

“(ダブルクォート)”でかこんだ文字列はChar型のリスト。[]でかこまれてるのがリストのしるし。

Prelude> :type "abc"
"abc" :: [Char]
Prelude> :type ['a','b','c']
['a','b','c'] :: [Char]
Prelude> :type "a"
"a" :: [Char]

なるほど。””でかこめば1文字でも要素ひとつのリストってわけか。
文字列を”でかこむとエラーになる。

Prelude> :type 'abc'
<interactive>:1:1: lexical error in string/character literal

GHCi

GHCiは対話的に操作できるインタプリタ。:help コマンドで使い方がわかる。

>ghci
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.4.1, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base-1.0 ... linking ... done.
Prelude> :help
 Commands available from the prompt:
   <stmt>                      evaluate/run <stmt>
   :add <filename> ...         add module(s) to the current target set
   :browse [*]<module>         display the names defined by    <module>
   :cd <dir>                   change directory to <dir>
   :def <cmd> <expr>           define a command :<cmd>
   :help, :?                   display this list of commands
   :info [<name> ...]          display information about the given  names
   :load <filename> ...        load module(s) and their dependents
   :module [+/-] [*]<mod> ...  set the context for expression evaluation
   :reload                     reload the current module set
   :set 

終了するには :quit。

関数の定義はできない。別途ファイルに保存して、:load コマンドで読み込む。

Prelude> :l addone.hs
Compiling Main             ( addone.hs, interpreted )
Ok, modules loaded: Main.
*Main> addOne 3
4