ポケモンリストから二匹のポケモンを一組とした組み合わせをつくりたい。 ただし、同じポケモン同士やタイプが同じポケモン同士は除外する(つまり、ペアとするポケモン同士は別のタイプを持つようにする)、という問題を考える。
環境の確認:
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.8.4
まずポケモンデータ型を定義しましょう。
$ ghci
Prelude> data Pokemon = Pokemon String String deriving (Show)
続いて pikachu ポケモンを生成して表示します。
Prelude> pikachu = Pokemon "Pikachu" "electric"
Prelude> pikachu
Pokemon "Pikachu" "electric"
うまくいきました。
ポケモンのタイプ部分は文字列ではなくそれ用のデータ型 Kind を定義することにします。
Prelude> data Kind = E | W | F deriving (Show,Eq)
E が electric で、W が water、F が fire タイプです。 あとで、タイプが同じか異なるか検査するつもりなので、Eq 派生をつけています。
それでは、この Kind を使って Pokemonデータ型の定義を修正して、再度 pikachu を生成。
Prelude> data Pokemon = Pokemon String Kind deriving (Show)
Prelude> pikachu = Pokemon "Pikachu" E
Prelude> pikachu
Pokemon "Pikachu" E
これで準備ができたので、ポケモンの種類を増やしてポケモンリストをつくります。
Prelude> squirtle = Pokemon "Squirtle" W
Prelude> charmander = Pokemon "Charmander" F
Prelude> golduck = Pokemon "Golduck" W
Prelude> pokemons = [pikachu, squirtle, charmander, golduck]
Prelude> pokemons
[Pokemon "Pikachu" E,Pokemon "Squirtle" W,Pokemon "Charmander" F,Pokemon "Golduck" W]
ゼニガメとヒトカゲ、ゴルダックを追加して、ピカチュウをあわせてリスト pokemons をつくりました。
それでは、applicative スタイルでポケモンの組合わせをつくります。
前回のエントリーで 表計算のセルアドレスを計算する方法をポケモン組み合わせに転用。
Prelude> pure (\a b -> (a,b)) <*> pokemons <*> pokemons
[(Pokemon "Pikachu" E,Pokemon "Pikachu" E),(Pokemon "Pikachu" E,Pokemon "Squirtle" W),(Pokemon "Pikachu" E,Pokemon "Charmander" F),(Pokemon "Pikachu" E,Pokemon "Golduck" W),(Pokemon "Squirtle" W,Pokemon "Pikachu" E),(Pokemon "Squirtle" W,Pokemon "Squirtle" W),(Pokemon "Squirtle" W,Pokemon "Charmander" F),(Pokemon "Squirtle" W,Pokemon "Golduck" W),(Pokemon "Charmander" F,Pokemon "Pikachu" E),(Pokemon "Charmander" F,Pokemon "Squirtle" W),(Pokemon "Charmander" F,Pokemon "Charmander" F),(Pokemon "Charmander" F,Pokemon "Golduck" W),(Pokemon "Golduck" W,Pokemon "Pikachu" E),(Pokemon "Golduck" W,Pokemon "Squirtle" W),(Pokemon "Golduck" W,Pokemon "Charmander" F),(Pokemon "Golduck" W,Pokemon "Golduck" W)]
とりあえず、全部の組み合わせ(ペア)ができました。
ただ、先頭のピカチュウ同士のように同じポケモン同士がペアになるのはなしにしましょう。
まず名前が同じだったら True を返す isSameName 関数を定義:
Prelude> isSameName (a,b) = (name a)==(name b) where name (Pokemon n _) = n
あとは先ほどの結果に対して filter する:
Prelude> filter (\pair-> not(isSameName pair)) $ pure (\a b -> (a,b)) <*> pokemons <*> pokemons
[(Pokemon "Pikachu" E,Pokemon "Squirtle" W),(Pokemon "Pikachu" E,Pokemon "Charmander" F),(Pokemon "Pikachu" E,Pokemon "Golduck" W),(Pokemon "Squirtle" W,Pokemon "Pikachu" E),(Pokemon "Squirtle" W,Pokemon "Charmander" F),(Pokemon "Squirtle" W,Pokemon "Golduck" W),(Pokemon "Charmander" F,Pokemon "Pikachu" E),(Pokemon "Charmander" F,Pokemon "Squirtle" W),(Pokemon "Charmander" F,Pokemon "Golduck" W),(Pokemon "Golduck" W,Pokemon "Pikachu" E),(Pokemon "Golduck" W,Pokemon "Squirtle" W),(Pokemon "Golduck" W,Pokemon "Charmander" F)]
さらに、同じタイプ同士もなしにしましょう。
そのために isSameKind 関数を定義します。
Prelude> isSameKind (a,b) = (kind a)==(kind b) where kind (Pokemon _ k) = k
では、isSameName, isSameKind でないポケモンペアだけを filter します。
Prelude> filter (\pair-> and [not(isSameName pair), not(isSameKind pair)]) $ pure (\a b -> (a,b)) <*> pokemons <*> pokemons
[(Pokemon "Pikachu" E,Pokemon "Squirtle" W),(Pokemon "Pikachu" E,Pokemon "Charmander" F),(Pokemon "Pikachu" E,Pokemon "Golduck" W),(Pokemon "Squirtle" W,Pokemon "Pikachu" E),(Pokemon "Squirtle" W,Pokemon "Charmander" F),(Pokemon "Charmander" F,Pokemon "Pikachu" E),(Pokemon "Charmander" F,Pokemon "Squirtle" W),(Pokemon "Charmander" F,Pokemon "Golduck" W),(Pokemon "Golduck" W,Pokemon "Pikachu" E),(Pokemon "Golduck" W,Pokemon "Charmander" F)]
これで、名前とタイプ(Kind)が異なるポケモンのペアのリストをつくりだすことができました。
ここまでの成果を main.hs にコードします。
data Kind = E | W | F deriving (Show,Eq)
data Pokemon = Pokemon String Kind deriving (Show)
isSameName (a,b) = (name a)==(name b)
where
name (Pokemon n _) = n
isSameKind (a,b) = (kind a)==(kind b)
where
kind (Pokemon _ k) = k
pikachu = Pokemon "Pikachu" E
squirtle = Pokemon "Squirtle" W
charmander = Pokemon "Charmander" F
golduck = Pokemon "Golduck" W
pokemons = [pikachu, squirtle, charmander, golduck]
pokemonPairs = pure (\a b -> (a,b)) <*> pokemons <*> pokemons
resultPokemonPairs = filter
(\pair-> and [not(isSameName pair), not(isSameKind pair)]) pokemonPairs
main :: IO ()
main = putStrLn $ show resultPokemonPairs
コンパイルして実行:
$ ghc main.hs
$ ./main
[(Pokemon "Pikachu" E,Pokemon "Squirtle" W),(Pokemon "Pikachu" E,Pokemon "Charmander" F),(Pokemon "Pikachu" E,Pokemon "Golduck" W),(Pokemon "Squirtle" W,Pokemon "Pikachu" E),(Pokemon "Squirtle" W,Pokemon "Charmander" F),(Pokemon "Charmander" F,Pokemon "Pikachu" E),(Pokemon "Charmander" F,Pokemon "Squirtle" W),(Pokemon "Charmander" F,Pokemon "Golduck" W),(Pokemon "Golduck" W,Pokemon "Pikachu" E),(Pokemon "Golduck" W,Pokemon "Charmander" F)]
できました。
ポケモン表示が冗長かつタイプがわかりづらいので、そこを修正します。
data Kind = E | W | F deriving (Eq)--(Show,Eq)
data Pokemon = Pokemon String Kind-- deriving (Show)
instance Show Kind where
show E = "electric"
show W = "water"
show F = "fire"
instance Show Pokemon where
show p = (name p) ++ "(" ++ (show $ kind p) ++ ")"
where
name (Pokemon n _) = n
kind (Pokemon _ k) = k
Kind, Pokemon データ型をそれぞれ Show のインスタンスにして、表示方法を指定しました。
それでは実行:
$ ghc main.hs; ./main
[(Pikachu(electric),Squirtle(water)),(Pikachu(electric),Charmander(fire)),(Pikachu(electric),Golduck(water)),(Squirtle(water),Pikachu(electric)),(Squirtle(water),Charmander(fire)),(Charmander(fire),Pikachu(electric)),(Charmander(fire),Squirtle(water)),(Charmander(fire),Golduck(water)),(Golduck(water),Pikachu(electric)),(Golduck(water),Charmander(fire))]
できました。
完成したコード全体を掲載します。
main.hs
data Kind = E | W | F deriving (Eq)
data Pokemon = Pokemon String Kind
instance Show Kind where
show E = "electric"
show W = "water"
show F = "fire"
instance Show Pokemon where
show p = (name p) ++ "(" ++ (show $ kind p) ++ ")"
where
name (Pokemon n _) = n
kind (Pokemon _ k) = k
isSameName (a,b) = (name a)==(name b)
where
name (Pokemon n _) = n
isSameKind (a,b) = (kind a)==(kind b)
where
kind (Pokemon _ k) = k
pikachu = Pokemon "Pikachu" E
squirtle = Pokemon "Squirtle" W
charmander = Pokemon "Charmander" F
golduck = Pokemon "Golduck" W
pokemons = [pikachu, squirtle, charmander, golduck]
pokemonPairs = pure (\a b -> (a,b)) <*> pokemons <*> pokemons
resultPokemonPairs = filter
(\pair-> and [not(isSameName pair), not(isSameKind pair)]) pokemonPairs
main :: IO ()
main = putStrLn $ show resultPokemonPairs
Makefile
run: main
./main
main: main.hs
ghc main.hs
以上です。