point-free style での書き方
Haskell で普通の記法から point-free style へと変形する方針について考えてみた.
きっかけは,この書き込み
http://pc12.2ch.net/test/read.cgi/tech/1242876647/11
module Main(main) where import Data.Char import Control.Monad import Data.List main = do { str <- getLine ; let xss = fun38 . map digitToInt $ str ; display xss ; putStr "last = " ; putChar . intToDigit . head . last $ xss } fun38 [] = [] fun38 xs = xs : fun38 (zipWith (\a b -> (a + b) `mod` 10) xs (tail xs)) display = zipWithM ((putStrLn .) . (. (intersperse ' ' . map intToDigit)) . (++) . flip replicate ' ') [0..]
これの zipWithM*1 の引数がぱっと見理解できなかったので,通常の記法からここまで行く自分なりの過程を辿ることにする.
方針としては,
f x = g $ h x f = g . h
と
f x = x + i f = (+ i)
と*2
f x y = g y x f = flip g
に基づいて変形していく.
普通に書けばこうだろう.
\i xs -> putStrLn $ (replicate i ' ') ++ (intersperse ' ' $ map intToDigit xs)
まずは xs を消すことから始める.これはそれほど複雑ではなく,
\i xs -> putStrLn $ (++) (replicate i ' ') (intersperse ' ' $ map intToDigit xs) \i xs -> putStrLn $ (++) (replicate i ' ') $ intersperse ' ' $ map intToDigit xs \i -> putStrLn . (++) (replicate i ' ') . intersperse ' ' . map intToDigit
となる.
ここで,簡単のために
f :: [Int] -> String f = intersperse ' ' . map intToDigit
とおいて話を進める.
\i -> putStrLn . (++) (replicate i ' ') . f \i -> putStrLn . (. f) ((++) (replicate i ' ')) \i -> (putStrLn .) $ (. f) ((++) (replicate i ' ')) \i -> (putStrLn .) $ (. f) $ (++) $ replicate i ' ' \i -> (putStrLn .) $ (. f) $ (++) $ flip replicate ' ' i (putStrLn .) . (. f) . (++) . flip replicate ' '
あとは f を戻せば
(putStrLn .) . (. (intersperse ' ' . map intToDigit)) . (++) . flip replicate ' '
と,最初の式になる.
はっきり言って,これくらいになってくると point-free style で書くべきではないでしょうね.
しかしまぁ,一種のパズルとして楽しむのもアリだと思います.
おまけ
なぜ display はこれほどまで複雑な point-free style で書いてあるのに,fun38 の zipWith はそうではないんだろうか…
これも上記と同様に point-free style に書き直せる.
fun38 :: [Int] -> [[Int]] fun38 [] = [] fun38 xs = xs : fun38 (zipWith ((flip mod 10 .) . (+)) xs (tail xs))