初めての投稿
こちらの記事を参考に、Hackage公開ではないHaskellパッケージを利用して、動かしてみる。
とにかく、超初心者だったけれど、stackと、それが提供してくれるデフォルトプロジェクトの作成方法、そこに出来上がるもの、.cabalファイルってどれ? Main.hsってどこ?などについての知識を仕入れた段階で挑戦。
環境
Ubuntu (使い慣れていません…)
準備
- Stackをインストールしておく。
- ライブラリのインストール(いつやったのか、覚えていない)
sudo apt install liblapack-dev libblas-dev
使いたいけれど、Hackageにないパッケージの入手
Hackageにあれば、.cabalファイルに書き込めば stack コマンドで取れるけれど、そうは行かないので、ローカルに取ってきて、そのローカルに置かれたパッケージを.cabalに指定することにする
git でパッケージを取得
~/test/ 以下で作業することとする
git clone https://github.com/adscib/monad-bayes
これで、~/test/monad-bayes/
ができる。この中に、monad-bayes.cabalファイルもあれば、src ディレクトリ、app ディレクトリもある。
この monad-bayes.cabalには山のように必要なモジュールを取るための情報が書かれている。
~/test/monad-bayes/
以下で次をやる。
stack haddock
stack haddock --open
これで、monad-bayesパッケージの情報をブラウザで見られるようになる。
実行環境の準備
自分でmonad-bayesパッケージを使って実行する環境を作る。
これも、stackを使って作る。
~/test/ で
stack new mbplay
する。
~/test/mbplay/
ディレクトリができ、その下に、
mbplay.cabalファイルができ、
~/test/mbplay/app/ 以下に、Main.hsができ
~/test/mbplay/src/ 以下に、Lb.hs ができる。
Main.hsは実行可能ファイルを作らせる「根っこ」のファイル。
srcディレクトリに、このmbplayプロジェクトのためのモジュールファイルを置くことにする。
mbplayのstack.yaml ファイルの書き換え
今、このプロジェクトのディレクトリは
~/test/mbplay/ であって、使いたいパッケージは
~/test/monad-bayes/ にあるので、
相対パスで、monad-bayesパッケージの頂上のディレクトリを指定する
packages:
- '.'
- location:
"../monad-bayes" # さっき clone したディレクトリ
extra-dep: true # 依存先(本体の一部じゃなくて)として扱ってもらう
mbplay のmbplay.cabal ファイルの書き換え
このプロジェクトで、ローカルに置いたmonad-bayes パッケージを使いたいので、それを指定する。
使う場所は、Main.hsかもしれないし、ローカルモジュールたちかもしれないので、mbplay.cabalファイルのそれぞれを管轄する部分にmonad-bayesを指定する。
~/test/mbplay/src/ 以下のローカルモジュールのファイル内でimportできるようにする。
library
hs-source-dirs: src
exposed-modules: Lib
build-depends: base >= 4.7 && < 5
, monad-bayes
default-language: Haskell2010
~/test/mbplay/app/ 以下にあるMain.hsでもimportできるようにする。
executable mbplay-exe
hs-source-dirs: app
main-is: Main.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, mbplay
, monad-bayes
default-language: Haskell2010
ここまでがうまく行っているかを確認
その1
この段階では、mbplayプロジェクトのMain.hsは、その中から、~/test/mbplay/Lib.hs をimportして、その中の関数を実行するようにできている。
~/test/mbplay/
以下で
stack install
とすると
mbplay-exe という実行可能ファイルができるが、まず、それをやってみる。
mbplay-exe
とやると、コンソールに"someFunc"という文字列が現れる。
その2
ついで、~/test/mbplay/Lib.hs
を次のように書き換える。
module Lib (someFunc) where
import Control.Monad.Bayes.Simple
このControl.Monad.Bayes.Simple は、今、使いたいと思っているmonad-bayesパッケージの中にあるものなので、このimportがうまく行けば、パッケージ利用環境はOKと解る。
この状態で
stack build
して、無事にコンパイルが終わればOK。
monad-bayesパッケージを利用
さて、本番。
monad-bayesパッケージを使うために、以下をする。
自作モジュールを作る
monad-bayesパッケージでは、
~/test/monad-bayes/test/
というディレクトリがあり、その中にいくつかのhoge.hsファイルがある。
これらは、~/test/monad-bayes/test/src/にあるわけではないので、モジュールとして参照できない(多分)。
なので、この~/test/monad-bayes/test/models/ 以下に置かれた Dice.hsというファイルを使った処理をmbplayの方でやって見ることにする。
~/test/mbplay 以下にいるとして、次のようなコマンドで
~/test/monad-bayes/models/Dice.hs
を
~/test/mbplay/src/Dice.hs としてコピーする。
# mbplay に今いるとして
cp ../monad-bayes/models/Dice.hs src/
この~/test/mbplay/src/Dice.hs を使うために、~/test/mbplay/mbplay.cabalを書き換える。
srcディレクトリの下にあるモジュールファイルのうち、使うのはDice.hsであって、Lib.hsでは無いので、exposed-modulesを書き換える。
library
hs-source-dirs: src
exposed-modules: Dice
build-depends: base >= 4.7 && < 5
, monad-bayes
default-language: Haskell2010
Main.hsを書き換える
さて。
Dice.hsを利用した処理を書く。
Dice.hsを使いつつ、それ以外にもいくつかのモジュールが必要でそれをimport指定しながら、
20個のサイコロを振って、その目の和を取ること、5000試行。
その結果をresultsに入れた後、
値ごとにソートして、隣同士で同じ値のものをグループ化して、その長さを求め、ヒストグラム様標準出力をさせている。
module Main where
import Dice
import Control.Monad.Bayes.Sampler
import Data.List (group, sort)
import Control.Monad (replicateM)
main :: IO ()
main = do
results <- sampleIO . replicateM 5000 $ dice 20
mapM_ putStrLn . hist . map length . group . sort $ results
-- create a very basic histogram.
-- `div` 2 . (+1) is just there to make the bars shorter.
hist :: [Int] -> [String]
hist = map (flip replicate '#' . (`div` 2) . (+1))
実行してみよう
~/test/mbplay/
にて
stack build
stack exec mbplay-exec
練習を兼ねて少し改変
引数をとって、サイコロの数、試行回数、描出ヒストグラムの打点スケール、以上3つを指定するようにする
getArgsで取り込んだargs の0,1,2番目の要素をそれぞれInt型でnarg0,narg1,narg2に持たせる。
module Main where
import Dice
import Control.Monad.Bayes.Sampler
import Data.List (group, sort)
import Control.Monad (replicateM)
import System.Environment
import System.IO
main :: IO ()
main = do
args <- getArgs
let narg0 = read (args !! 0) :: Int
let narg1 = read (args !! 1) :: Int
let narg2 = read (args !! 2) :: Int
results <- sampleIO . replicateM narg0 $ dice narg1
--results <- sampleIO . replicateM 5000 $ dice 20
--mapM_ putStrLn . hist . map length . group . sort $ results
mapM_ putStrLn . (hist2 narg2) . map length . group . sort $ results
-- create a very basic histogram.
-- `div` 2 . (+1) is just there to make the bars shorter.
hist :: [Int] -> [String]
hist = map (flip replicate '#' . (`div` 2) . (+1))
hist2 :: Int -> [Int] -> [String]
hist2 x = map (flip replicate '#' . (`div` x) . (+1))
stack build
stack exec mbplay-exe 100000 10 200