ushidayの日記

主に「IBMi」のメモに・・・

Haskell読書会@ぐぐれ浜松が終わりました

第8回Haskell勉強会@ShizuDevで、無事スピーカーが終わりました。
ぐぐれ浜松...噂にたがわぬ、強者でした。それはコレを見て頂ければ、わかるでしょう...

紆余曲折ありましたが、実際には無事にプロジェクタも手に入り、1時間遅れぐらいでスタートしました。
自分の話の内容は、あまり大した事ではないので割愛いたしますが、以前取り上げたWindows」環境で、ANSIを使う場合に、ansi-terminal内の標準関数が用意されている事が分かりましたので、この場を借りて訂正します。
■カーソルポジションと画面クリア

-- nasi-terminalのインポート
import System.Console.ANSI

-- 画面クリア
clearScreen

-- カーソル位置(1,1)が左上
setCursorPosition 1 1

こんな感じでansi-terminalが入っていれば簡単に出来ます。

次回は初の富士開催です。みんなでB級グルメの”つけナポリタン”を、お昼に食べたい方は、am11:30に、岳南鉄道吉原本町駅に集合です。

Windows環境でプログラミングHaskell第9章をやる為の準備

ShizuDevのHaskell勉強会で、プログラミングHaskell第9章”対話式プログラム”のスピーカーをやらせて頂きます。

第9章の中でターミナルの制御に、エスケープシーケンスを使っており、Windows環境の方は、「出来なくなってしまうのでは?」と思い調べましたら、ansi-terminalというGHCのライブラリがありましたので、それを入れる事により、ターミナル制御用の関数を提供してくれます。Hugsの方はスミマセン。

勉強会まで時間が無いという事もあり、取り急ぎやり方を羅列しておきます。

検証した環境 : Windows XP SP3 ,GHC-6.10.4

GHCは恐らく、6.10.x系は大丈夫と思われます。GHC-6.12.x系は動きません。自分がそうでした。ディレクトリ構造を見たら、10から12で大きく変わってました。

cabal-installを入れる

ghcのパッケージインストールツールである”cabal-install”を入れます。
cabal-installのページ
cabal-installのダウンロードページ

ダウンロードページから、”cabal-install tool (version 0.8.2)”と書いてある下の”cabal.exe”をダウンロードします。

%HASKELL_HOME%\binに、先程ダウンロードした、”cabal.exe”をコピー or 移動します。%HASKELL_HOME%はGHCのインストールディレクトリです。PATHに%HASKELL_HOME%\binを通すのは、Windowsインストーラがしてくれているハズです。

cabalの動作確認

以下はcabalの代表的な操作です。

#バージョン確認
cabal --version

#リポジトリリストの更新
cabal update

#cabalに対応するパッケージを確認
cabal list 

# インストール済みのパッケージ
cabal list --installed

#何が起こるか事前確認が出来る
cabal install パッケージ名 --dry-run 

#パッケージのインストール
cabal install パッケージ

#更新可能パッケージの確認
cabal upgrade --dry-run

#パッケージの更新
cabal upgrade

    • 次に、「cabal update」でcabalのリポジトリリストを更新します。更新が完了すると「C:\Documents and Settings\ユーザ\Application Data\cabal」にディレクトリが、出来てここにパッケージが入ります。


ansi-terminalのインストール

ソースは勉強会、当日にもお知らせしますので、Windowsの方は、環境だけでも作っておいてください。
お忙しいとは思いますがお願い致します。

以下は、本に記載で使えない関数の代表的な代替え関数です。


カーソル位置と画面クリアは、勉強会の結果、ansi-terminalで、簡単に出来る事が分かりましたので、こちらで訂正しております。

代替えの関数の抜粋


カーソルポジション用「setCursorPosition」関数

■Win32ANSI.hs(本はputStr "\ESC[x;yH")

{-# LANGUAGE ForeignFunctionInterface#-}
module Win32ANSI (setCursorPosition) where

import System.IO
import System.Win32.Types

import Data.Bits

import Foreign.C.Types
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import Foreign.StablePtr

import Control.Concurrent.MVar
import Control.Exception (bracket)

import GHC.IOBase (Handle(..) ,Handle__(..) ,FD )

hSetCursorPosition :: Handle
                   -> Int 
                   -> Int 
                   -> IO ()
setCursorPosition :: Int 
                  -> Int 
                  -> IO ()

setCursorPosition = hSetCursorPosition stdout

withHandle :: Handle -> (HANDLE -> IO a) -> IO a
withHandle handle action = do
    hFlush handle
    withHandleToHANDLE handle action

adjustCursorPosition :: HANDLE -> (SHORT -> SHORT -> SHORT) -> (SHORT -> SHORT -> SHORT) -> IO ()
adjustCursorPosition handle change_x change_y = do
    screen_buffer_info <- getConsoleScreenBufferInfo handle
    let window = csbi_window screen_buffer_info
        (COORD x y) = csbi_cursor_position screen_buffer_info
        cursor_pos' = COORD (change_x (rect_left window) x) (change_y (rect_top window) y)
    setConsoleCursorPosition handle cursor_pos'

hSetCursorPosition h y x = withHandle h $ \handle -> adjustCursorPosition handle (\window_left _ -> window_left + fromIntegral x) (\window_top _ -> window_top + fromIntegral y)




type SHORT = CShort

type UNPACKED_COORD = CInt

unpackCOORD :: COORD -> UNPACKED_COORD
unpackCOORD (COORD x y) = (fromIntegral y) `shiftL` (sizeOf x * 8) .|. (fromIntegral x)


peekAndOffset :: Storable a => Ptr a -> IO (a, Ptr b)
peekAndOffset ptr = do
    item <- peek ptr
    return (item, ptr `plusPtr` sizeOf item)

pokeAndOffset :: Storable a => Ptr a -> a -> IO (Ptr b)
pokeAndOffset ptr item = do
    poke ptr item
    return (ptr `plusPtr` sizeOf item)


data COORD = COORD {
        coord_x :: SHORT,
        coord_y :: SHORT
    }

instance Show COORD where
    show (COORD x y) = "(" ++ show x ++ ", " ++ show y ++ ")"

instance Storable COORD where
    sizeOf ~(COORD x y) = sizeOf x + sizeOf y
    alignment ~(COORD x _) = alignment x
    peek ptr = do
        let ptr' = castPtr ptr :: Ptr SHORT
        x <- peekElemOff ptr' 0
        y <- peekElemOff ptr' 1
        return (COORD x y)
    poke ptr (COORD x y) = do
        let ptr' = castPtr ptr :: Ptr SHORT
        pokeElemOff ptr' 0 x
        pokeElemOff ptr' 1 y


data SMALL_RECT = SMALL_RECT {
        rect_top_left :: COORD,
        rect_bottom_right :: COORD
    }

rect_top, rect_left, rect_bottom, rect_right :: SMALL_RECT -> SHORT
rect_top = coord_y . rect_top_left
rect_left = coord_x . rect_top_left
rect_bottom = coord_y . rect_bottom_right
rect_right = coord_x . rect_bottom_right

instance Show SMALL_RECT where
    show (SMALL_RECT tl br) = show tl ++ "-" ++ show br

instance Storable SMALL_RECT where
    sizeOf ~(SMALL_RECT tl br) = sizeOf tl + sizeOf br
    alignment ~(SMALL_RECT tl _) = alignment tl
    peek ptr = do
        let ptr' = castPtr ptr :: Ptr COORD
        tl <- peekElemOff ptr' 0
        br <- peekElemOff ptr' 1
        return (SMALL_RECT tl br)
    poke ptr (SMALL_RECT tl br) = do
        let ptr' = castPtr ptr :: Ptr COORD
        pokeElemOff ptr' 0 tl
        pokeElemOff ptr' 1 br


data CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO {
        csbi_size :: COORD,
        csbi_cursor_position :: COORD,
        csbi_attributes :: WORD,
        csbi_window :: SMALL_RECT,
        csbi_maximum_window_size :: COORD
    }
    deriving (Show)

instance Storable CONSOLE_SCREEN_BUFFER_INFO where
    sizeOf ~(CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size)
      = sizeOf size + sizeOf cursor_position + sizeOf attributes + sizeOf window + sizeOf maximum_window_size
    alignment ~(CONSOLE_SCREEN_BUFFER_INFO size _ _ _ _) = alignment size
    peek ptr = do
        (size, ptr1) <- peekAndOffset (castPtr ptr)
        (cursor_position, ptr2) <- peekAndOffset ptr1
        (attributes, ptr3) <- peekAndOffset ptr2
        (window, ptr4) <- peekAndOffset ptr3
        maximum_window_size <- peek ptr4
        return (CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size)
    poke ptr (CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size) = do
        ptr1 <- pokeAndOffset (castPtr ptr) size
        ptr2 <- pokeAndOffset ptr1 cursor_position
        ptr3 <- pokeAndOffset ptr2 attributes
        ptr4 <- pokeAndOffset ptr3 window
        poke ptr4 maximum_window_size

foreign import stdcall unsafe "windows.h GetConsoleScreenBufferInfo" cGetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO BOOL

foreign import stdcall unsafe "windows.h SetConsoleCursorPosition" cSetConsoleCursorPosition :: HANDLE -> UNPACKED_COORD -> IO BOOL
foreign import stdcall unsafe "windows.h SetConsoleTitleW" cSetConsoleTitle :: LPCTSTR -> IO BOOL



getConsoleScreenBufferInfo :: HANDLE -> IO CONSOLE_SCREEN_BUFFER_INFO
getConsoleScreenBufferInfo handle = alloca $ \ptr_console_screen_buffer_info -> do
    failIfFalse_ "getConsoleScreenBufferInfo" $ cGetConsoleScreenBufferInfo handle ptr_console_screen_buffer_info
    peek ptr_console_screen_buffer_info


setConsoleCursorPosition :: HANDLE -> COORD -> IO ()
setConsoleCursorPosition handle cursor_position = failIfFalse_ "setConsoleCursorPosition" $ cSetConsoleCursorPosition handle (unpackCOORD cursor_position)


foreign import ccall unsafe "_get_osfhandle" cget_osfhandle :: FD -> IO HANDLE

withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a
withHandleToHANDLE haskell_handle action = 
    withStablePtr haskell_handle $ const $ do
        let write_handle_mvar = case haskell_handle of
                FileHandle _ handle_mvar     -> handle_mvar
                DuplexHandle _ _ handle_mvar -> handle_mvar
        
        fd <- fmap haFD $ readMVar write_handle_mvar
        
        windows_handle <- cget_osfhandle fd
        
        action windows_handle

withStablePtr :: a -> (StablePtr a -> IO b) -> IO b
withStablePtr value = bracket (newStablePtr value) freeStablePtr

■画面消去

import System.Cmd (system)


cls            :: IO ()
cls            =  do system("cls")
                     return ()


■ビープ音

beep                          :: IO ()
beep                          =  do putStr "\BEL"
                                    hFlush stdout


■標準入力読取(入力した文字を表示させない)

getCh                         :: IO Char
getCh                         =  liftM (chr . fromEnum) c_getch
foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt

Haskell読書会8th@ぐぐれ浜松 開催のお知らせ

早いもので、ShizuDevのHaskell読書会も第8回を迎えます。Haskell読書会も残すところあと5回です。次のテーマは...なんて話も最近はチラホラ。

話は戻りますが、ShizuDev初の西部開催で浜松となります。

浜松と言えば餃子...懇親会は紅虎餃子房で、食べ放題です。(第9回は富士で、Haskell読書会9th@つけナポリタンを予定w)

スピーカは、私がやらせて頂く事になっております。

読書会という特性上、途中参加は難しい面もあるかと思いますが、静岡の技術者交流の場として、是非ご参加下さい。

ShizuDev haskellサイト

以下の要領で第八回Haskell読書会を開催いたします。

参加者は、one drinkをオーダーして頂く必要がありますが、勉強会の参加費は無料となっておりますので、こぞってご参加下さい。

●開催要領
・日時: 2010年8月28日(土) 13:00〜17:00
・場所:場所:ぐぐれ浜松2階http://web-cci.jp/ggl/
※階段が判りづらい場所にあります。一時1階集合という事でお願い致します。
・地図: http://bit.ly/bIjs13
・定員: 20名
・費用: 無料ですが、各自1ドリンクをオーダーして頂く必要がございます。
・持ち物: パソコン(練習問題をやるのに使用)、課題図書
・課題図書: プログラミングHaskellISBN:4274067815)※各自事前購入
・環境: OS依存はしないが、GHC又はHugsを事前導入しておく事
・範囲: 9 章
・担当: うしだ
・懇親会 : 紅虎餃子房 浜松店 http://www.hotpepper.jp/strJ000030987/map/
・懇親会費: 一般 4,000 円、学生・未成年 2,000 円 程度の予定

・登録フォーム:http://bit.ly/ShizuDevHaskell08Entry
・登録状況確認:http://bit.ly/ShizuDevHaskell08EntryList

Haskell読書会5th@ShizuDevをやりました

毎月恒例の第5回Haskell読書会を静岡で開催致しました。
今回はプログラミングHaskellの第6章”再帰関数”です。早いもので、読書会も折り返し地点になりました。
スピーカーは、私が産声をあげた地”豊橋”から、いつも来て頂いている”ふるたさん”に、やって頂きました。名古屋からもわざわざ”クラ●△◇×◎※さん”が、初参加で来て頂きました。東京や名古屋の都市圏から、静岡の勉強会に来て頂けるなんて感無量です。ふるたさん&関係者&参加者のみなさん、ありがとうございました。

内容は参加メンバーの方々が、既にブログでまとめて下さっているので、そちらをご参考に...。

再帰自体は、自身を呼び出したり、相互呼び出ししたりと、難しいお話ではないのですが、今回の章は”何が何でも再帰的”な部分がありました。しかし実際は再帰を知る事により、この後「あそこでは再帰でやったけど、こうやればもっと効率よく解りやすいよ」的な展開になって行くのかな?と個人的には思っております。

再帰関数は、最終的に基底部に、落ち着かないと、永久ループになってしまうので、いかにその基底部のパターンマッチが、パッとスッキリ思いつくかが勝負だと思いました。逆にパターンマッチが直ぐに思いつかないケースは、変テコな実装を考えてしまう事も?...。
再帰の利用ケースは、例題や解説でもそうですが、やはりリスト等の繰り返し処理が多いと思います。Haskellをこれまで勉強してきて、”リスト処理が簡潔にこなせる”(特に部分的に関数を作って、最終的に出来た関数は)という印象がさらに強まった感じです。

不思議な感覚なのは”( n + k )パターン”を使った再帰です。以前の章で「n+kパターンは、将来的に無くなるかも...。あまり積極的に使わない方が...。」みたいなこと書いてあったのですが、今章はバンバン出てきます。例えば2つ整数を引数に取って、積を返す関数”mult”は...。

■(n+k)パターンの例

mult::Integer->Integer->Integer
mult 0 _ = 0
mult _ 0 = 0
mult x 1 = x
mult 1 y = y
mult x (y+1) = x + mult x y

と書けるのですが、xの加算をy回繰り返すのに、(y+1)で受けるという文化が、まだ少し馴染めません。
ついこう書きたくなってしまいます。

mult x y = x + mult x (y-1)

でもむしろ(n + k)パターンの方が、いかにも”Haskell”らしくて好きです。「ザクとは違うんだよ。ザクとは。」みたいな。(でも、非推奨の記法かもしれませんがね...。)

懇親会では富士の「B級グルメ”つけナポリタン”食べるぞ!勉強会」の話で盛り上がっていたので、富士での開催もありそうです。次回は2回目の三島です。その次は、静岡でガンダム公開初日に、ガンダムのある東静岡でやる予定です。その次は浜松。静岡県を東へ西へShizuDevは活動していきます。

今回は、業務連絡が行き届いていなくて、おやつが普段の2倍になってしまいました。その分糖分補給もいつもの2倍。みなさんの頭も2倍スッキリしたカモ?
■今回のおやつ(ドーナツ&磯自慢入チーズケーキ)

Haskell読書会4th@ShizuDev

早いものでShizuDev主催のHaskell読書会も第四回を迎える事が出来ました。今回はin三島という事で、初の東部開催です。段取りなどは、東部の方に、御協力を頂いた事に感謝しております。場所は三島の商工会議所で、非常に設備も良いところで、ぜひまた使わせて頂きたいと思いました。今回使用した部屋以外にも、ディスカッションやロールプレイングに使えそうな部屋もあり、少数名でディスカッション形式の勉強会も面白いなと思いました。

スピーカーは、静岡IT Proの代表でもある、id:ftnkさんで、第五章のテーマは「リスト内包表記」についてです。締めはシーザー暗号のクラックまでと、一つのテーマなのですが、演習問題は意外と骨の有る章でした。
今回の勉強会で、今までと少し違ったスタイルだったのは、課題図書を読み上げた所までで、出来る演習問題は、こなしてしまうという手法です。過去の勉強会は、最後に纏めて演習問題だったのですが、記憶に焼き付いて間もなく演習問題をする方が、効率も良く、些細な疑問も処理し易かったように思えます。自分の時は、「このスタイルでイコウ」と思いました。

個人的にはループ処理のガチガチ頭の人間からすると、リスト内包表記の妙味というか、そう言ったものを感じ取れました。
例えば、サイコロの出目のパターンをRPGでループ処理した時、
RPGの場合

%subarr(xs:1) ='123456';
%subarr(ys:1) ='123456';
i = 0;
for ix1 = 1 to 6 ;
    for ix2 = 1 to 6 ;
         i+=1;
         ptn(i).x = xs(ix1);
         ptn(i).y = ys(ix2);
    endfor;
endfor;

は、こんな感じで、手続き型言語で見る典型的な例かと思います。次にHaskellのリスト内包表記
Haskellの場合

[(x,y)|x<-[1..6],y<-[1..6]]

ループの入れ子が、ワンライナーで書けてしまうところが、レガシー人間からするとただただ羨ましい。(最初にforeachを見た時も少し感動したぐらいですから)
このリスト内包表記の入れ子は、カンマ区切りでさらに拡張できます。たとえば3つの入れ子ならば、

[(x,y,z)|x<-xs,y<-ys,z<-zs]

となります。さらに抽出条件まで付けてしまう事が出来ます。例えば,x,y,zの合計が20以上のみリストとするみたいな場合

[(x,y,z) | x<-xs,y<-ys,z<-zs,sum([x,y,z]) >=20]
or
[(x,y,z) | x<-xs,y<-ys,z<-zs,(x+y+z) >=20]

実に不思議だと感じたのは、ただカンマ区切りにしただけで、何故にココまで高機能なのか?もちろん実装がそうなるように工夫してあるといえばそうなのだけど、演習問題でリスト内包表記の入れ子が出てきた時に、こう感じました。「これはある意味シンタックスシュガーなのでは?」と...。

前述の以下の表記は、
■例1

[(x,y)|x<-[1..6],y<-[1..6]]
や
[(x,y,z)|x<-[1..6],y<-[1..6],z<-[1..6]]

こう書く事も出来る
■例2

concat [[(x,y)|y<-[1..6]]|x<-[1..6]]
や
concat ( concat [[[(x,y,z)|z<-[1..6]]|y<-[1..6]]|x<-[1..6]] )

リスト内包表記を1つのシンタックスとして捉えた時、通常入れ子を表現する場合、実は例2の方がごく自然な形で、ただ入れ子にすればする程、concatが必要になり、リスト表現としても分かりづらくなる、そこで工夫されたのが、例1の様なシンタックスシュガーなのかと、勝手に理解しました。普通に考えて、例1が使えれば例2は複雑なだけで、通常使う場面がないなと思うほど良く出来ていて、あえてソレを解らせるための演習問題?と考えると奥が深いなと思い知らされました。
色々な意味で、さらにHaskellの世界に引き込まれていく予感が...。

今回のおやつは、メンバー中最年少高校生のSupaki君が、かの遠州銘菓”たこまん”で買ってきてくれました。”たこまん”の名前だけはよく見るのですが、食べるのは初めてで、ブッセ風の美味しいオヤツをありがとう。

■おやつ

Haskell読書会4th@三島 開催のお知らせ

第四回Haskell読書会を開催致します。
今回は、初の東部(三島)開催です。
東部のメンバーの方々も多いので、盛り上がって、今後定期的な開催が出来ればといいなぁと思っております。
懇親会は一次会の方は調整中ですが、二次会は雰囲気のイイBarを代表がリサーチしてくれてあるので楽しみです。と思ったが、車で行くから飲めないジャン!!(;_;)
本題の方ですが、スピーカーは静岡IT Pro代表のとなかさん(id:ftnkさん)がやってくれます。内容はリスト内包がメインです。おっと早く勉強しなきゃですが...。開催要項は以下の通りです。メンバーが固まりつつありますが、自分も含めHaskell未経験者も多いので、途中参加も歓迎致します。Haskellだけでなく静岡ITの開発系技術者が語れる場としていけたら良いと思ってます。

以下の要領で第四回Haskell読書会を開催いたします。

申し込みフォームリンク「http://bit.ly/dn6qRn
※こちら「http://bit.ly/d1Cte2」で登録状況の確認が出来ます。

●開催要領
・日 時 : 2010年4月24日(土) 13:00〜17:00
・場 所 : 三島商工会議所会館 4階会議室B
・地 図 : http://www.mishima-cci.or.jp/profile.html
・定 員 : 20名
・費 用 : 一般 1000円,学生・未成年:無料
・持ち物 : パソコン(練習問題をやるのに使用)、課題図書
・課題図書: プログラミングHaskellISBN:4274067815)※各自事前購入
・環境  : OS依存はしないが、GHC又はHugsを事前導入しておく事
・範 囲 : 5 章
・担 当 : となかさん
・懇親会 : 調整中
・懇親会費: 一般 4,000 円、学生・未成年 2,000 円 程度の予定

shizu-dev Haskell読書会最新情報「http://bit.ly/d8qca0

第三回Haskell読書会

第三回Haskell読書会が静岡市民文化会館で、開催され無事終了致しました。
スピーカーのid:kynbitさん、高校生という若さでLTをしてくれたSupakiくん、関係者並びにご参加頂いた皆様ありがとうございました。
ついつい話が脱線しがちな中、kynbitさん曰く、「今回の第四章のテーマは、あくまでも”関数の定義する方法を学ぶ”」という、かの名言で直ぐに軌道修正。前回よりも会場利用時間が、30分短いという中、ハンズオンを中心にまとめて頂き、充実していたのではと思います。

関連ブログ

本章の内容については、参加メンバーが既に、ブログで書かれているので、そちらで雰囲気を感じて頂けたらと。


おやつ

2開催連続で、代表のたかはしさんが、タマゴ型プリン&たまごボーロを、仕入れてきてくれました。ありがとうございます。そしてウマイ!頭使って糖分補給コレ基本です。たまごボーロも懐かしいです。口の中の水分みんな持っていかれる感じが何とも言えない。
(「eatたまごボーロ :: たまごボーロ → 口の中砂漠化」「drinkお茶 :: お茶 → 口の中オアシス」)


SupakiくんLT

最年少のSupaki君が、TwitterをテーマにLTをやってくれました。
Twitterのキモは”おやじギャグなんだ!”と教わりました。高校生にして、アラサーやアラフォーのオッサン相手に、OpenOfficeでスライド作るなんてすごい経験だよ。将来有望です。

練習問題1を2次会で「あーだこーだ」

本章の練習問題1は以下の様な内容でした。

要素数が偶数のリストを引数にとる関数「halve」は、要素数の真ん中で2分割したリストのタプルを返す。
つまり...
halve [1,2,3,4,5,6]
>([1,2,3],[4,5,6])
になれば良いと

kynbitさんから、追加の課題で「奇数の時は、リストを返さないでエラーを...」。しかし終了時間を迎え、急いで片付け、そのまま懇親会へ。そして2次会で、「奇数でエラーって型宣言しているから、空リストのタプル返すくらい?」みたいな話で、「まぁ、それだけでもやってみようYo!」とバーでPC引っ張り出してきて「あーだこーだ」。(何やってんだろ?)
Haskellとしては(関数型としては?)、当たり前の事が、やはりいざキーボード叩いてみると、上手くいかなかったり、面白い発見もあったり、とりあえずの結論が出たところで、2次会は解散。そんな中で、where節を改めて考えてみました。

バーで追加課題をやっていた時、まずこういう書き方で、Parseエラー

-- parse error on input `|'
halveBar::[a]->([a],[a])
halveBar xs | (length xs `mod` 2) == 0 = (take (myLen) xs ,drop (myLen) xs)
                where  myLen  =  length xs `div` 2
            | otherwise =  ([],[])

halveBar::[a]->([a],[a])
halveBar xs | (length xs `mod` 2) == 0 = (take (myLen) xs ,drop (myLen) xs)
            | otherwise =  ([],[])
                where  myLen  =  length xs `div` 2

こっちはOK。where節を細かくやっていないので、自分が語ると間違っていそうで、アレなのですが、調べてみると...。

    • where節は、直前の束縛の右辺に対して作用する。
    • where節は、直前の束縛より深いインデントでなければならない。

確かにガードは、「if then else if then else ...〜」のシンタックスシュガーみたいなモノだから、前の例だとthenとelseの間にイキナリwhereを入れている様なもので、よくよく考えればおかしい。つまりこうしているのと同じ

halveBar::[a]->([a],[a])
halveBar xs = if (length xs `mod` 2) == 0 
                    then  (take (myLen) xs ,drop (myLen) xs)
                            where  myLen  =  length xs `div` 2   -- if ならせんヤロ
                    else  ([],[])

halveBarの束縛に対して、whereで局所定義するなら、普通にこうなる。

halveBar::[a]->([a],[a])
halveBar xs = if (length xs `mod` 2) == 0 
                    then  (take (myLen) xs ,drop (myLen) xs)
                    else  ([],[])
                            where  myLen  =  length xs `div` 2

ifだと間違えない、それはそんな処にwhere入れたら、違和感を感じるから。でもガードみたいな見慣れない、シンタックスを拝んだりすると、変な錯覚をしてしまい、違和感を感じない。(そもそもガードの書き方に違和感を感じたり。見やすくて好きなんですけどね、^_^ガード。自分ガード派です。今のところ...)あーダメな自分を再認識しました。そして理解した上でまとめ。

halve::[a]->([a],[a])
halve xs | myEven = (take myHalfLen xs ,drop myHalfLen xs)
         | otherwise =  ([],[])
  where 
     myLen      =  length xs                     --リストの長さ
     myHalfLen  =  myLen `div` 2                 --リストの半分の長さ
     myEven     |  (myLen `mod` 2 ) == 0 = True  --要素数の偶数判定
                |  otherwise = False

whereは直前の束縛の右辺に対して作用とあるが、上の記述は正しく動いているので、同一where内の束縛に対しても有効みたい。(myLenはmyEvenなどでもスコープ出来ているので...。)これは、結果論なので、本当に正しいのかはチト不安な気もします。
スコープでダメな例は以下のような場合。

--     Not in scope: `myHalf'
--   Failed, modules loaded: none.

halveNG::[a]->([a],[a])
halveNG xs | myEven = (take myHalfLen xs ,drop myHalfLen xs)
           | otherwise =  ([],[])
  where 
     myLen      =  length xs
     myHalfLen  =  myLen `div` myHalf  -- myHalfはスコープできない。
     myEven     |  (myLen `mod` myHalf ) == 0 = True
                |  otherwise = False
        where myHalf = 2

この場合は、それぞれwhere節が下のように、必要になる。

halveNG::[a]->([a],[a])
halveNG xs | myEven = (take myHalfLen xs ,drop myHalfLen xs)
           | otherwise =  ([],[])
  where 
     myLen      =  length xs
     myHalfLen  =  myLen `div` myHalf
        where myHalf = 2
     myEven     |  (myLen `mod` myHalf ) == 0 = True
                |  otherwise = False
        where myHalf = 2

今回はひょんなことから、where節の理解が少し深まりました。次回の第4回は三島で行うことに決定しました。また正式に決まり次第告知させて頂きます。
2010.7.24(土)のグランシップ開催だけは、リアルガンダム東静岡に初お披露目とあって、早速会場をキープしました。;-)