読者です 読者をやめる 読者になる 読者になる

IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

HaskellのCSV読み込み

利用ライブラリ

Text.CSV

インポート

Prelude> import Text.CSV

1行読み込み

Prelude Text.CSV> parseCSVTest "aaa,bbb"
[["aaa","bbb"]]
Prelude Text.CSV> parseCSVTest "aaa,bbb,ccc"
[["aaa","bbb","ccc"]]

トリムはしない

Prelude Text.CSV> parseCSVTest "aaa,bbb,  ccc"
[["aaa","bbb","  ccc"]]

複数行読み込み

Prelude Text.CSV> parseCSVTest "aaa,bbb,  ccc\naaa,"
[["aaa","bbb","  ccc"],["aaa",""]]
Prelude Text.CSV> parseCSVTest "aaa,bbb,  ccc\naaa,,bbb"
[["aaa","bbb","  ccc"],["aaa","","bbb"]]

出力

Prelude Text.CSV> printCSV [["1","2","3"],["4","5"],["6"]]
"\"1\",\"2\",\"3\"\n\"4\",\"5\"\n\"6\""

読み込み件数取得

Prelude Text.CSV> parseCSVFromFile "./data/UNdata_Export_pork.csv" >>= \x ->  either (\x -> return 0) (return . length) x
1376

10行目出力

Prelude Text.CSV> parseCSVFromFile "./data/UNdata_Export_pork.csv" >>= \x -> return $ either (\x -> []) (\x -> x !! 10) x
["Albania","2009","Thousand metric tons","0.63",""]

全件出力

Prelude Text.CSV> either (\x -> putStrLn "error") (mapM_ (putStrLn . show)) =<< parseCSVFromFile "./data/UNdata_Export_pork.csv"
["Country or Area","Year","Unit","Value","Value Footnotes"]
["Albania","2013","Mil. USD","3.86207176515949",""]
["Albania","2013","Thousand metric tons","0.8",""]

ServantのFileUploadサンプル

package

servant-multipart: multipart/form-data (e.g file upload) support for servant

$ cabal install servant-multipart 
Resolving dependencies...
Notice: installing into a sandbox located at
foo/.cabal-sandbox
Downloading http-client-0.5.6.1...
Configuring natural-transformation-0.4...
Configuring http-client-0.5.6.1...
Building natural-transformation-0.4...

...

sample

servant file upload sample

client

$ curl -i -X PUT http://127.0.0.1:8081/file -F "upfile1=@MyWebApp_upload.hs" -F "xx=yy"
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Tue, 14 Mar 2017 12:03:05 GMT
Server: Warp/3.2.9
Content-Type: application/json;charset=utf-8

{"msg":"\"upfile doesn't exist\"","len":-1}
$
$ curl -i -X PUT http://127.0.0.1:8081/file -F "upfile=@MyWebApp_upload.hs" -F "xx=yy"
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Tue, 14 Mar 2017 12:03:13 GMT
Server: Warp/3.2.9
Content-Type: application/json;charset=utf-8

{"msg":"uploaded","len":1633}
$ ls -al MyWebApp_upload.hs 
-rw-r--r--  1 foo bar  1633  3 4 11:01 MyWebApp_upload.hs
$ 

server log

*Main> main
FileData {fdInputName = "upfile1", fdFileName = "MyWebApp_upload.hs", fdFileCType = "application/octet-stream", fdFilePath = "/var/folders/hw/p4bp49hd7v9_1j60sjvmhqnr0000gn/T/servant-multipart1974930991404280278.buf"}
not exist
FileData {fdInputName = "upfile", fdFileName = "MyWebApp_upload.hs", fdFileCType = "application/octet-stream", fdFilePath = "/var/folders/hw/p4bp49hd7v9_1j60sjvmhqnr0000gn/T/servant-multipart8933518161505795335.buf"}

HaskellでYamlの任意項目の取り扱い

Applicativeスタイルで.*?で任意項目、.!=でデフォルト値を設定します。`

parseJSON (Object m) = AppConfig <$> m .:? "port" .!= 8081 -- default

a sample of reading a yaml file by Haskell

ghciのプロンプトをいい感じにする方法

いつも忘れるので備忘

coderwall.com

>:set prompt "\ESC[34mλ> \ESC[m"
λ> aa

<interactive>:28:1: error: Variable not in scope: aa

λ> :set prompt "\ESC[1;34m%s\n\ESC[0;34mλ> \ESC[m"
*Main Control.Monad.Morph Control.Monad.Trans Control.Monad.Trans.Maybe Control.Monad.Trans.State

tidalをreplから使う

1: SuperColliderを起動する。

2: SuperDirtを起動する。

SuperDirt.start;

3: ghciを起動、ライブラリの取り込み

$ cabal repl
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
Prelude> import Sound.Tidal.Context  -- インポート

4: repl上でコーディングする

Prelude Sound.Tidal.Context> :t bpsUtils  -- 時間を取得する
bpsUtils :: IO (Double -> IO (), IO Rational)
Prelude Sound.Tidal.Context> let s = bpsUtils


Prelude Sound.Tidal.Context> :t superDirtSetters  -- パターン関数を取得する
superDirtSetters
  :: IO Time
     -> IO
          (ParamPattern -> IO (),
           (Time -> [ParamPattern] -> ParamPattern) -> ParamPattern -> IO ())
Prelude Sound.Tidal.Context> let dd = s >>= \x -> superDirtSetters (snd x)

Prelude Sound.Tidal.Context> :t sound
sound :: Pattern String -> ParamPattern
Prelude Sound.Tidal.Context> :t atom
atom :: a -> Pattern a
Prelude Sound.Tidal.Context> :t sound . atom -- 文字列からパターンを作成する
sound . atom :: String -> ParamPattern

Prelude Sound.Tidal.Context> dd >>= \x -> fst x (sound (atom "bd")) -- バスドラが鳴る

(solved) The pkg-config package 'libpcre' is required but it could not be found.

pkg-confgとして認識されているか確認する。

$ pkg-config --libs libpcre
Package libpcre was not found in the pkg-config search path.
Perhaps you should add the directory containing `libpcre.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libpcre' found

profileの編集

$ vim ~/.bash_profile 

.bash_profileに下記を追加。

export PKG_CONFIG_PATH=/usr/lib/pkgconfig

パスを登録

source ~/.bash_profile 

認識されていることを確認

$ pkg-config --libs libpcre
-lpcre  

再インストール

$ cabal install pcre-light-0.4.0.4 --reinstall
Resolving dependencies...
In order, the following will be installed:
pcre-light-0.4.0.4 (via: mysql-simple-0.4.0.1) (new version)
mysql-simple-0.4.0.1 (via: persistent-mysql-2.6) (reinstall) (changes: pcre-light-0.4.0.3 removed)
persistent-mysql-2.6 (reinstall) (changes: mysql-simple-0.4.0.1 removed)
Warning: Note that reinstalls are always dangerous. Continuing anyway...
Notice: installing into a sandbox located at
/work03/webapp/servant04/.cabal-sandbox
Configuring pcre-light-0.4.0.4...
Building pcre-light-0.4.0.4...
Installed pcre-light-0.4.0.4
Configuring mysql-simple-0.4.0.1...
Building mysql-simple-0.4.0.1...
Installed mysql-simple-0.4.0.1
Configuring persistent-mysql-2.6...
Building persistent-mysql-2.6...
Installed persistent-mysql-2.6
Updating documentation index
/work03/webapp/servant04/.cabal-sandbox/share/doc/x86_64-osx-ghc-8.0.1/index.html

ServantのFormサンプル

POSTFormデータを送信するServantの実装サンプルを、本家サイトも含めて探していたのですが、見つけられませんでした。結果的にはとっても簡単でした。FromFormインスタンスにするだけでした。

data Res = Res { ret :: Int} deriving (Eq, Show, Generic) -- Formデータを受け取るデータ構造

instance FromForm Req --- FromFormのインスタンスにする。

Servant Form Sample

実行

$ curl -w '\n' 'http://localhost:8081/add' --data 'x=1&y=1' -XPOST
{"ret":2}

送信したデータの型が不正の場合は400となる。

$ curl http://127.0.0.1:8081/add -d "x=1&y=a" -XPOST
could not parse: `a' (input does not start with a digit)