Home About Contact
Haskell

Haskellでポケモン組み合わせ問題を解く

ポケモンリストから二匹のポケモンを一組とした組み合わせをつくりたい。 ただし、同じポケモン同士やタイプが同じポケモン同士は除外する(つまり、ペアとするポケモン同士は別のタイプを持つようにする)、という問題を考える。

環境の確認:

$ 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

以上です。