fugafuga.write

日々のログ



すごいH本 part31

今年も終わりですね

モジュールの続きをやっていく

各桁の数の合計が40になる最初の自然数を求める関数の実装

  1. 数値を文字列に変換
  2. 文字列をリスト化
  3. 文字を数に変換
  4. リストの合計を計算

このように処理する。

Data.Char.digitToIntで文字を数値に変換する('0'-'9','A'-'F'に対して動作する。小文字可)

*Main> digitToInt '3'
3

数を引数にとって、各桁の数の合計を返す関数

import Data.List
import Data.Char

digitSum :: Int -> Int
digitSum = sum . map digitToInt . show

実行結果

*Main> digitSum 12345
15

この関数を使って各桁の合計が40になる数を探すために、Data.List.findを使う

*Main> :t find
find :: Foldable t => (a -> Bool) -> t a -> Maybe a

1つめの引数に述語をとり、2つめの引数にリストをとる。

関数が返すMaybe aは、0個かちょうど1個の要素だけを持てる。この型は失敗する可能性があることを表現するのに使われる。

Nothingは何も持っていないという値を作るときに使い、Justは何かを保持している値を作るときに使う。

*Main> Nothing
Nothing
*Main> Just 3
Just 3
*Main> :t Nothing
Nothing :: Maybe a
*Main> :t Just 3
Just 3 :: Num a => Maybe a
*Main> :t Just True
Just True :: Maybe Bool

findで述語を満たす要素が見つかった場合、その要素をJustでラップしたものが返される。見つからなければNothingが返る。

*Main> find (>5) [1,2,3,4,5,6]
Just 6
*Main> find (>5) [1,2,3,4,5]
Nothing

上記をふまえ、各桁の合計が40になる数を求める関数を実装する

firstTo :: Int -> Maybe Int
firstTo n = find (\x -> digitSum x == n) [1..]

実行結果

*Main> firstTo 40
Just 49999

所感

Maybe便利。関数を自分で書くのはまだ難しい。読めるようにはなっているけど。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part30

モジュールを使う。の続き

foldlがスタックオーバーフローを起こすことがあるのでそれを見てゆく。

*Main> foldl (+) 0 (replicate 100000000 1)
*** Exception: stack overflow

Haskell は遅延評価なので実際の値の計算は可能な限り後まで引き伸ばされる。 評価するための計算をメモリ上に保持していくので計算量が多くなりすぎるとメモリ上限に達する。

foldl の計算の様子

foldl (+) 0 [1,2,3] =
foldl (+) (0 + 1) [2,3] =
foldl (+) (0 + 1 + 2) [3] =
foldl (+) (0 + 1 + 2 + 3) [] =
((0 + 1) + 2) + 3 =
(1 + 2) + 3 =
3 + 3 =
6 

スタックオーバーフローさせないようにするために、Data.List.foldl'という正格バージョンの関数が用意されている。

*Main> foldl' (+) 0 (replicate 100000000 1)
100000000

foldl1に対するfoldl1'なども用意されている。

所感

畳み込みの計算の様子がわかりやすいし、遅延評価の仕組みが知れた。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part29

モジュールを使う。の続き

Data.Charを使ってシーザー暗号でメッセージを暗号化し、他人に読めないようにする関数を実装する。

シーザー暗号は、各文字をアルファベット上で一定の数だけシフトするという暗号化方法。

以下の関数を使う

Data.Char.ord

文字を対応する数値に変換する

*Main Lib> Data.Char.ord 'a'
97

Data.Char.chr

ordの逆のことをする

*Main Lib> Data.Char.chr 97
'a'

実装する

import Data.Char

encode :: Int -> String -> String
encode offset msg = map (\c -> chr $ ord c + offset) msg

実行結果

*Main> encode 2 "google"
"iqqing"

複合する関数を実装する

decode :: Int -> String -> String
decode shift msg = encode (negate shift) msg

実行結果

*Main> decode 2 "iqqing"
"google"

所感

シーザーサラダ

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part28

引き続きモジュールの使用例

2つのリストを受け取り、1つめのリストが2つめのリストに含まれているかどうかを調べる関数を実装してゆく。

以下の関数を使う

Data.List.tails

リストを受け取り、tailsを繰り返し適用する

*Main Data.List> tails "yahooooooooo"
["yahooooooooo","ahooooooooo","hooooooooo","ooooooooo","oooooooo","ooooooo","oooooo","ooooo","oooo","ooo","oo","o",""]
*Main Data.List> tails [1,2,2,3,4,5,6]
[[1,2,2,3,4,5,6],[2,2,3,4,5,6],[2,3,4,5,6],[3,4,5,6],[4,5,6],[5,6],[6],[]]

Data.List.isPrefixOf

2つのリストをとり、2つめのリストが1つめのリストで始まっているかチェックする

*Main Data.List> "foo" `isPrefixOf` "f"
False
*Main Data.List> "foo" `isPrefixOf` "foooooo"
True

Data.List.any

こんなの

*Main Data.List> any (>3) [1,2,3,4,5]
True
*Main Data.List> any (>3) [1,2]
False

組み合わせる

import Data.List

isIn :: (Eq a) => [a] -> [a] -> Bool
needle `isIn` haystack = any (needle `isPrefixOf`) (tails haystack)

実行結果

*Main Data.List> [6] `isIn` [1,2,3,4,5,6]
True
*Main Data.List> [7] `isIn` [1,2,3,4,5,6]
False
*Main Data.List> "ooo" `isIn` "yahoooooooo"
True
*Main Data.List> "ooo" `isIn` "yaho"
False

こうすると型が違うのでエラーになる

*Main Data.List> 6 `isIn` [1,2,3,4,5,6]

<interactive>:36:1: error:
    • No instance for (Num [a0]) arising from the literal ‘6’
    • In the first argument of ‘isIn’, namely ‘6’
      In the expression: 6 `isIn` [1, 2, 3, 4, ....]
      In an equation for ‘it’: it = 6 `isIn` [1, 2, 3, ....]

ここで実装したisInisInfixOfという関数で定義されている。

所感

型制約の付け方がよくわからんので、自分で書くとなった場合に迷いそうである。実際、今回実装を予想して書いたら、型制約が抜けてた。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る