ログ日記

作業ログと日記とメモ

関数型プログラミングの考え方を理解するための昔の記事

この前
関数型プログラミングはまずは純粋関数型言語を用いて、考え方から理解しよう
関数型プログラミングはまず考え方から理解しよう - Qiita
ここを読んだ。

関数型プログラミングの考えを学ぶには、純粋関数型言語で学ぶべきである

これは同意なんだけど、サンプルコードがしっくり来ない。


こんなに複雑になる?というかオブジェクト指向のコードを関数型っぽく変換ということに無理があるような。
仕様を満たすコードをゼロから考えたら、全然変わってくるような。

ということでちょっとHaskellで書いてみた。

data Dish = Karaage deriving Eq
data Bento = Bento Dish Int deriving Eq

単純な数値のリストかタプルでもいいんだけど、オブジェクト指向に寄せてデータ構造を書く。

eat :: Bento -> Bento
eat (Bento s a) = Bento s (a - 1)

replaceList :: Bento -> [Bento] -> [Bento]
replaceList m a = replace a
  where
    replace [] = []
    replace (x:xs)
      | m == x = eat x:xs
      | otherwise = x:replace xs

「データを1減らす」という処理と「リストを一つ入れ替える」という処理を書く。
「リストから最大のものを1件探す」という処理は maximumが使えるので、そのまま使う。

それからユーティリティ的な関数、「xに関数fをn回適用した結果を返す」を作る。

nest :: (a -> a) -> a -> Int -> a
nest f x n = (iterate f x) !! n


最後に

bentoList :: [Bento]
bentoList = [Bento Karaage 10, Bento Karaage 8, Bento Karaage 6]

main :: IO ()
main = putStrLn $ show $ nest (maximum >>= replaceList) bentoList 5

一気に関数を接続して、計算。
※ n回繰り返したときの処理ログが欲しいわけではなく、最後の結果が欲しいということだと読み取ったので、途中経過は出力しない。


関数型プログラミングっていうかHaskell独自のことかもしれないけど、とりあえず細かい関数のパーツを作っておいて、繋ぎ合わせて大きな機能を作るのが関数型プログラミングの考え方っぽいと思える。



昔見たブログのやり取りはとても良かった。
Haskell でグローバル変数が欲しい理由 - あどけない話
グローバル変数が欲しい理由?http://web.archive.org/web/20100702191552/http://www.sampou.org/cgi-bin/haskell.cgi?%A5%B0%A5%ED%A1%BC%A5%D0%A5%EB%CA%D1%BF%F4%A4%AC%CD%DF%A4%B7%A4%A4%CD%FD%CD%B3%A1%A9
「グローバル変数が欲しい理由?」の考察 - あどけない話
URLが変わっちゃったので再度メモ。





以下全体のコード。

module Main where

data Dish = Karaage deriving Eq
data Bento = Bento Dish Int deriving Eq

instance Show Dish where
  show Karaage = "唐揚げ"
instance Show Bento where
  show (Bento a b) = show a ++ show b ++ "個"
instance Ord Bento where
  compare (Bento Karaage a) (Bento Karaage a') = compare a a'

nest :: (a -> a) -> a -> Int -> a
nest f x n = (iterate f x) !! n

eat :: Bento -> Bento
eat (Bento s a) = Bento s (a - 1)

replaceList :: Bento -> [Bento] -> [Bento]
replaceList m a = replace a
  where
    replace [] = []
    replace (x:xs)
      | m == x = eat x:xs
      | otherwise = x:replace xs

bentoList :: [Bento]
bentoList = [Bento Karaage 10, Bento Karaage 8, Bento Karaage 6]

main :: IO ()
main = putStrLn $ show $ nest (maximum >>= replaceList) bentoList 5