200 - 10を求めるお話

# `200 - 10`を求めるお話


## お題
+ Haskellを用い、できるだけたくさんの方法で`200 - 10`を求めてみます。

(ちなみに、このお題の他にbonus track(2の階乗のリスト：`[1,2,4,8,16,32,64,128,256,512,1024]`を得る方法)も補足に書き書きしましたので、そちらも合わせてご覧いただければ。) 



## 動機
[すごいHaskellたのしく学ぼう!](https://www.amazon.co.jp/すごいHaskellたのしく学ぼう-Miran-Lipovača/dp/4274068854) p.64より何種類くらい作れるか気になった[^disparate]。割り算は型的に面倒くさそうと思ったので、もっとシンプルな題材に変更した！ 半分は備忘録のため、冗長な部分もかなり残してあります。

Note) 私のレベル自体はAdvanced beginner(http://qiita.com/lotz/items/0d68c8440d1f362d0c32 )だと思います。なので、その範囲くらいで。

[^disparate]: あと、この本は読み物としては良書なんだけど、欲しい情報があっちこっちに飛び過ぎでいる気がする。なので、なにか縦横無尽にHaskellの文法を駆使する必要のあるお題を探していたのであります。

## ルール
+ 答えが出れば、速度は無視です。

+ [190]と出てもOKとします(`head [190]`で一般性を失わない).`Just 190`とかは`190:[]`とモナドというくくりでは共通しているので、それはなしで。

+ `200 - 1 - 1 - 1..`とか、演算そのものを変えてしまうのは✖︎。あくまで`200 - 10`という演算をしているということを意味している式を書くこと。(`-10 + 200`の解釈はOKとしよう.`0`をinitとして、`-10`と`+200`をたたみ込む動作は際どいから△マークをつけます) 

+ ワンライナーでかけるものだけOKとします[^curly]。

[^curly]: とはいうものの、セミコロン、カーリーブレイス({})を用いると、普通なら複数行で書くコードが一行で書けたりするので、そこのところはやりすぎないようにしてます。

+ syntax sugarで実質同じものとかあるけど、気にしない方向で。

Note) 自身はstack(Haskellのパッケージをビルドしたりインストールしたりする便利ツール)を用いています。`stack exec ghci`で対話的インタプリタ ghciが起動できます。

## スタート！

```hs
> 200 - 10
-- (200-) :: Num a => a -> a
-- 2項演算子では、演算子の左右どちらかに引数を与えたうえで
-- ()で囲んでも関数にできる(セクション)([1, p.64] or [4, p.161]) 
> (200-) 10
-- (+) :: Num a => a -> a -> a
> (+) 200 (-10)
> (-) 200 10
> sum [200, -10]

-- マイナス記号はsubtract関数でも表現出来る([1, p.64])
-- (-)演算子とは第一引数と第二引数の順序が逆であることに注意
> subtract 10 200
-- flip関数は[1, p.67]参照
> flip subtract 200 10
-- subtract関数 を'-'とすると、(-10) :: Intと認識されるので、✖︎ ([1, p.64])
> (subtract 10) 200

-- 中置記法([1, p.3])
> 10 `subtract` 200
> (`subtract` 200) 10


-- $:関数適用演算子[1, p.83]
> (+ 200) $ -10
-- ポイントフリースタイル[1, p.87]も用いてみる　際どいので一応△で。
> (+ 200) . negate $ 10

-- ($) operator(関数適用演算子, [1, p.84, p.240])
-- ($ 200) は (\f -> f 200)と同じ。
> ($ 200) (subtract 10) 
> ($ 200) $ subtract 10 

-- List Comprehension (リスト内包表記[1, p.15])
> [200 - x | x <- [10]] 
> [x-y | x<-[200], y <-[10]]

-- with map function [1, p.68]
> map (200 -) [10]
-- リストはFunctor型クラスで、fmapの定義はmapと同等([1, p.152])
> fmap (200 -) [10]
-- [1, p.232] (->)(関数作用）そのものもファンクタ。
-- :t fmap (+ 200) (negate) :: Num b => b -> b
> fmap (+ 200) (negate) 10
-- 際どいから△
> fmap (subtract 10) (+200) $ 0

-- with ($) operator [1, p.84]
-- this expression equals map ($ 200) [(subtract 10)]
-- ($ 200) は (\f -> f 200)と同じ。
> map ($ 200) $ map subtract [10]
> map ($ 200) $ map (flip (-)) [10]

-- with lambda expression ([1, p.73])
> (\x -> subtract x 200) 10
> (\x y -> x - y) 200 10
-- タプルもラムダ式中に入れこめる
> (\(a, b) -> (a - b)) (200, 10)

-- with let expression ([1, p.46])
> let fn = \x -> 200 - x in map fn [10]
> let fn = \f -> f 200 in fn $ subtract 10
-- with where keyword([1, p.43])
> let y = map f [10] where f = \x -> 200 - x in y
-- [2, p.68] カーリーブレイスを用いて、束縛式を複数書ける
> let {x = 200; y = 10} in x - y
-- 汚いけれど、一行に無理やり押し込んでみる
-- let {変数1 = 式1; 変数2 = 式2; ...} in 式n([3]の再帰定義の章を参照)
> let {x = 200; y = 10; fn :: Int -> Int -> Int; fn a b = a - b} in fn x y



-- 畳み込み
-- using foldl or foldr function([1, p.77])
> let z = 200 in foldl (\acc x -> acc ++ [z-x]) [] [10]
> let res = foldl fn [] [10] where fn acc x = acc ++ [200-x] in res
> let z = 200 in foldr (\x acc ->  (z-x) : acc) [] [10]
-- foldl (\acc x -> acc - x) 200 [10] と同じ。(accはアキュムレーター(結果の値))
> foldl (-) 200 [10]
-- foldr (\x acc -> x - acc) 0 [200, 10] と同じ
> foldr (-) 0 [200,10]
> foldr (flip (-)) 200 [10]
-- セコイ気がする..△かな？ [1, p.355] 参照 (idは恒等関数)
> let f = foldr (.) id [(+200)] in f $ -10
-- もっとセコイ気がする...△
> let f = foldr (.) id [(+200), (subtract 10)] in f 0

-- 左側の要素から評価される
-- たとえば、 foldl1 (-) [200, 10, 500] は ((-) ((-) 200 10) 500)と同じ。
> foldl1 (-)  [200, 10]
-- 右側の要素から評価される
-- 例えば、 foldr1 (-) [500, 200, 10] は ((-) 500 ((-) 200 10))と同じ。
> foldr1 (-) [200, 10]

-- [1, p.66]
> zipWith (-) [200] [10]


-- Applicative Functor(アプリカティブファンクター)
-- [1, p.243]
-- fs <*> xs = [f x | f <- fs, x <- xs] ([1, p.245]参照)
> fmap (-) [200] <*> [10]
> pure (-) <*> [200] <*> [10]
-- :: (Applicative f, Num b) => f b
> pure (-) <*> pure 200 <*> pure 10
-- (<$>)の詳細は[1, p.244]参照
> (-) <$> [200] <*> [10]
-- liftA2 f a b = f <$> a <*> b -- 上と同じ[1, p.252]
> liftA2 (-) [200] [10]
-- △
> let fn = (+) <$> (+200) <*> (subtract 10) in fn 0
-- ZipListについては[1, p.250]参照。 import Control.Applicative が必要
> getZipList $ (-) <$> ZipList [200] <*> ZipList [10]
-- [1, p.254]
> sequenceA [(200-)] 10


-- モノイドが助けに来た！[1, p.270]
-- import Data.Monoid が必要
> getSum $ Sum 200 `mappend` Sum (-10)
> getSum . mconcat . map Sum $ [200, -10]
-- foldMapについては、[1, p.278] moduleのimportは不要。
> getSum $ foldMap Sum [200, -10]

-- モナド計算
> return 200  >>= \x -> [x-10]
-- '=<<'を使えば逆順でかける
> (\x -> [x - 10]) =<< return 200

-- 200をxに束縛し、10をyに束縛する
-- return 200 >>= (\x -> [10] >>= (\y -> [x - y])) 
> return 200 >>= \x -> [10] >>= \y -> [x - y]
-- :: (Num b, Monad m) => m b
> return 200 >>= \x -> return 10 >>= \y -> return (x - y)
-- 無理やり複数行を一行に押し込める part2
> do {x <- return 200; y <- return 10; return (x - y)}
-- monadの合成(<=<)[1, p.312]
-- import Control.Monad が必要
-- h :: (Num c, Monad m) => c -> m c
> let h = (\x -> return (x+200)) <=< (\x -> return (x-10)) in h 0

-- sequence [(-200)] 10と同じ。リスト内の要素のモナドを一つ一つ走査。
-- sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
-- Preludeの状態であればimportは特に不要。
> sequence (map (-) [200]) 10

-- Writer monadを使用(今回の計算の場合、Writer monadを使用する利点はないので△)
-- import Control.Monad.Writer が必要
> let calc = writer (200, [show 200]) >>= \x -> writer (10, [show 10]) >>= \y -> return (x - y) :: Writer [String] Int in fst $  runWriter calc


-- (->)(関数作用)そのものもMonadであることを利用(Reader monad)
-- △
> let fn = (+200) >>= \x -> (subtract 10) >>= \y -> return (x+y) in fn 0
-- import Control.Monad.Reader が必要
-- △
> let fn = reader (+200) >>= \x -> reader (subtract 10) >>= \y -> return (x + y) in runReader fn 0

-- liftMはfmapのモナド拡張バージョン[1, p.344]
-- import Control.Monad が必要
> liftM (200-) [10]
-- [1, p.345] apは、Applicative Functorにおける<*>のモナド拡張バージョン
> return (-) `ap` [200] `ap` [10]
```

## 番外編：文字列そのまま処理する

ここからは、少しずるいですが...

```hs
-- words : import Data.List が必要
-- if式はelseが必須なので、case式を用いる。
-- [2, p.68] カーリーブレイスを用いて、束縛式を複数書ける
> let {[sa, sop, sb] = words "200 - 10"; op n = case n of "-" -> (-)} in (op sop) (read sa) $ read sb
```

## 参考
+ https://en.wikibooks.org/wiki/Haskell/Syntactic_sugar ... Haskellのsyntax sugar

リスト内包表記の上の例だと、

```hs
> [x-y | x <- [200], y <- [10]]
> do {x <- [200]; y <- [10]; return (x - y)}
```

は`[200] >>= \x -> [10] >>= \y -> [x-y]`のsyntax sugarになります。


---

うーん、こんなもんかなぁ。思ったよりもたくさん書けた！(何個かせこいのあるけど笑)

---



# 補足



## 2の階乗をいろんな方法で求めてみる[^mot]

+ 上記のコードとややかぶるところがあるので、補足とさせていただきます。
+ `[1,2,4,8,16,32,64,128,256,512,1024]`を得ることを目標とします。
+ scanlを用いるところはcoolかも？

[^mot]: 理系の方なら、「1+1は？」 「2！」「2+2は？」 「4！」的な遊び？をやったことある人は多いのではないでしょうか。


## スタート！

```hs
-- List Comprehension (リスト内包表記)
> [2^x | x <- [0..10]]
> [y^x | x <- [0..10], y <- [2]]


-- with map function
> map ($ 2) $ map (flip (^))  [0..10]
-- [1, p.64] セクション
> map (2^) [0..10]
> take 11 $ map (2^) [0..]
> let fn n = map (2^) $ [0..n] in fn 10
> let fn n = map (2^) $ enumFromTo 0 n in fn 10
-- ポイントフリースタイル
> let fn = map (2^) . enumFromTo 0 in fn 10



-- with zipWith function
> zipWith (^) (repeat 2) [0..10]
> zipWith (^) [2,2..] [0..10]
> map (\(x,y) -> y^x) $ zip [0..10] $ repeat 2
-- [1, p.251] import Control.Applicative が必要。
> getZipList $ (^) <$> ZipList (repeat 2)  <*> ZipList [0..10]


-- $:関数適用演算子
> map ($ 2) $ map (flip (^)) [0..10]

-- foldr
> foldr (\x acc ->  (2^x) : acc) [] [0..10]

-- scanl([1, p.82])
> scanl (*) 1 $ replicate 10 2
-- truncate : 整数値にしたい
> map (truncate) $ scanr (flip (/)) 1024 $ replicate 10 2

-- Applicative functor
> pure (2^) <*> [0..10]
> pure (^) <*> pure 2 <*> [0..10]
-- (<$>)の詳細は[1, p.244]参照
>  (^) <$> pure 2 <*> [0..10]
> map (flip (^)) [0..10] <*> pure 2
-- [1, p.252]
> liftA2 (^) (pure 2) [0..10]
-- sequenceAは、Applicative Functorバージョンの関数
> sequenceA (map (flip (^)) [0..10]) 2

-- Monad
> return 2 >>= \x -> [0..10] >>= \y -> return (x^y)
-- [1, p.345]
-- import Control.Monad が必要
> return (^) `ap` return 2 `ap` [0..10]
-- fmap のMonad拡張バージョン
> liftM (2^) [0..10]
-- importは特に不要 リスト中のモナドを順に捜査する
> sequence (map (flip (^)) [0..10]) 2
```


# 参考文献
1. [すごいHaskellたのしく学ぼう!](https://www.amazon.co.jp/%E3%81%99%E3%81%94%E3%81%84Haskell%E3%81%9F%E3%81%AE%E3%81%97%E3%81%8F%E5%AD%A6%E3%81%BC%E3%81%86-Miran-Lipova%C4%8Da/dp/4274068854)

2. [Real World Haskell―実戦で学ぶ関数型言語プログラミング](https://www.amazon.co.jp/Real-World-Haskell―実戦で学ぶ関数型言語プログラミング-Bryan-OSullivan/dp/4873114233)

3. http://www.geocities.jp/m_hiroi/func/haskell.html

4. [関数プログラミング実践入門](https://www.amazon.co.jp/関数プログラミング実践入門-──簡潔で、正しいコードを書くために-WEB-PRESS-plus/dp/4774169269)