より良い環境を求めて このページをアンテナに追加 RSSフィード

2015-07-13

[] 頭の体操

CodeIQ ホリエモンからの挑戦状

https://codeiq.jp/magazine/2015/07/26213/



何故か体調を崩して頭ふらふらのときに解説編の記事を見てHaskellでコードを書いていたので記念メモ。

module Main where

import Control.Applicative
import Data.Set (fromList, member)

main::IO ()
main = prog <$> getInputs >>= print
-- main = print $ prog (10000, [1..5000])

prog :: (Int, [Int]) -> Int
prog (ln, xs) = length $
     [(a, b, ln - a - b) | a <- [x | x <- xs],
                           b <- [x | x <- xs,
                                     a < x,
                                     x < ln - a - x,
                                     member (ln - a - x) $ fromList xs]
     ]

getInputs :: IO (Int, [Int])
getInputs = (,) <$> readLn <*> (readLn >>= readContents)

readContents :: Int -> IO [Int]
readContents n = take n <$> map read <$> lines <$> getContents

ghcの環境が壊れててそれを戻すだけで1時間以上かかった記憶がある。あとリスト操作部分よりもIO操作とApplicativeの使い方調べるのに時間がかかって、問題にある「想定時間10分」は余裕で超えた。1時間以上はかかった気がする。

そして https://gist.github.com/yancya/3c5fa7c0f09ad85f5230 こちらのRubyより遅い。リストのindexを使ったりListじゃないものを使えば速くなりそうだけども放置で。


http://www.sampou.org/cgi-bin/haskell.cgi?%A5%B0%A5%ED%A1%BC%A5%D0%A5%EB%CA%D1%BF%F4%A4%AC%CD%DF%A4%B7%A4%A4%CD%FD%CD%B3%A1%A9

こちらを読み返しつつの練習。

2012-03-27

[] persistentで自動生成されたコードを見る

Template Haskellで生成されたコードを見る手順メモ。


http://www.yesodweb.com/book/persistent

ここのサンプルをファイルに保存する。分かりやすいようにpersistの定義は別関数にする。

runPersist = [persist|
Person
    name String
    nick String
    age Int Maybe
BlogPost
    title String
    authorId PersonId
|]

そしてghciで開く。


*Main> :t runPersist
runPersist :: [EntityDef]

*Main> let expr = share [mkPersist sqlSettings] runPersist
Loading package persistent-sqlite-0.8.0 ... linking ... done.

*Main> :t expr
expr
  :: Language.Haskell.TH.Syntax.Q [Language.Haskell.TH.Syntax.Dec]

*Main> :m +Language.Haskell.TH
*Main Language.Haskell.TH> 

*Main Language.Haskell.TH> :t runQ expr
runQ expr :: Language.Haskell.TH.Syntax.Quasi m => m [Dec]

*Main Language.Haskell.TH> q <- runQ expr
*Main Language.Haskell.TH> :t q
q :: [Dec]

*Main Language.Haskell.TH> ppr q
instance Database.Persist.Store.PersistField (PersonGeneric backend)
    where sqlType _ = Database.Persist.Store.SqlString
... 以下出力

2012-03-08

[] yesod のリロード機能を再実装する

昨日 *1 はYesodからファイルを持ってきたが、ここでしていることは関連ファイルを監視して変更があればrunghcを実行するというもの。


何をやっているのかを理解するために、リロードする部分だけ抜き出して写経した。


yesod のコードから cabal を使わないようにして依存関係の処理を削っただけ。


動かすにはdevel.hsが必要。

module Main where

import Prelude
import Network.Wai (Application)
import Network.Wai.Handler.Warp
    (runSettings, defaultSettings, settingsPort)
import Control.Concurrent (forkIO)
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
import Application
import Reload (develLoop)


main :: IO ()
main = do
  putStrLn "Starting devel application on localhost:3000"
  _ <- forkIO $ runSettings defaultSettings
    { settingsPort = 3000 } getApplicationDev
  develLoop

getApplicationDev :: Application
getApplicationDev = logStdoutDev getApplication

dist/devel-terminate のファイル名が気になったので、昨日のコードからloop関数を Reload.hs に移動した。

Application.hs は昨日のコードをそのまま利用。

runghc Reload

とすると、ghc devel.hs でコンパイル → runghc devel.hs が動き出す。

.hs を変更したら それを検知して上の処理がもう一度走る。

2012-03-07

[] Yesod の 動的リロードの仕組みだけ利用する

wai-handler-devel がエラーでインストールできない。

どうやら削除された Network.Wai.Middleware.Debug を参照している模様。

https://github.com/yesodweb/wai/issues/25

https://github.com/yesodweb/wai/blob/master/wai-handler-devel/Network/Wai/Handler/DevelServer.hs

yesod は独自コマンドで開発サーバーを立ち上げるようになったので、もう wai-handler-devel は古くなっているのかもしれない(バージョンは更新されているが)


そもそも動的リロードはどうやっているのだろうと思って探した。

どうやら https://github.com/yesodweb/yesod/tree/master/yesod ここの Devel.hs と Build.hs のようだ。

Devel.hs は シンプルに devel 関数だけ提供しているので、このファイルだけあれば良さそう。

これらのファイルを作業ディレクトリにコピーして、リロード可能な単純なプログラムを書いてみる。


共通のmain.hs(暫定テスト用)と devel.hs ファイル。

main.hs

import Devel

main :: IO ()
main = devel False []

とりあえずdevel関数ベタ書きで。


devel.hs

{-# LANGUAGE PackageImports #-}
module Main where

import Prelude
import Network.Wai (Application)
import Network.Wai.Handler.Warp
    (runSettings, defaultSettings, settingsPort)
import Control.Concurrent (forkIO)
import Control.Concurrent (threadDelay)
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
import System.Directory (doesFileExist)
import System.Exit (exitSuccess)
import Application


main :: IO ()
main = do
  putStrLn "Starting devel application on localhost:3000"
  forkIO $ runSettings defaultSettings
    { settingsPort = 3000 } getApplicationDev
  loop

loop :: IO ()
loop = do
  threadDelay 100000
  e <- doesFileExist "dist/devel-terminate"
  if e then terminateDevel else loop

terminateDevel :: IO ()
terminateDevel = exitSuccess

getApplicationDev :: Application
getApplicationDev = logStdoutDev getApplication

devel.hs が無いと怒られるので。だいたいyesodのテンプレートのまま。


Application.hs

{-# LANGUAGE OverloadedStrings #-}
module Application (getApplication) where

import Prelude
import Network.Wai (Application, Request, responseLBS, pathInfo)
import qualified Network.HTTP.Types as HT
import qualified Data.ByteString.Lazy.UTF8 as LBS
import qualified Data.Text as T
import Data.Monoid (Monoid (mappend))

infixr 5 <>
(<>) :: Monoid m => m -> m -> m
(<>) = mappend

getApplication :: Application
getApplication req = return $  responseLBS HT.status200 [] $ contents req

contents :: Request -> LBS.ByteString
contents req = "Path Info is " <> parse req <> "!"

parse :: Request -> LBS.ByteString
parse req = textToBS $ T.concat (pathInfo req)

textToBS :: T.Text -> LBS.ByteString
textToBS t = LBS.fromString $ T.unpack t

PATH_INFO を表示するだけのアプリケーション。


cabalファイルも無いと怒られるので暫定でyesodコマンドで生成したものから持ってくる。

other-modules のセクションは全て削除。その他 build-depends もほとんど削除(別にそのままでも動く)。


runghc main.hs

を実行してサーバーが起動することを確認する。

ここでApplication.hsを変更したら再読み込みされた。


なんか遠回りしてる気がしないでもない。

2012-02-28

[] Debian に yesod を入れる

使っているマシンがsqueezeでghcのバージョンが古くてcabalがエラーを吐いたので、GHC7とHaskell-Platformのソースをダウンロードする。

http://www.haskell.org/ghc/download_ghc_7_0_4

http://hackage.haskell.org/platform/linux.html

まずHaskell Platformのページを見てGHCのバージョンを合わせる。


必要なlibglut3-devパッケージも入れる。

http://haskell.g.hatena.ne.jp/udzura/20110926#1317034260


以下su省略。

tar xvjf ghc-7.0.4-x86_64-unknown-linux.tar.bz2
cd ghc-7.0.4/
./configure
make install
cd ..

tar xvzf haskell-platform-2011.4.0.0.tar.gz
cd haskell-platform-2011.4.0.0/
./configure
make
make install

yesodのインストールは

http://www.yesodweb.com/page/five-minutes

ここに書いてある通り。

~/.cabal/bin にPATHを通してから

cabal update
cabal install Cabal cabal-install yesod

たぶんcabal installで自動的に入りそうだが関係ライブラリを入れておく。

http://www.sampou.org/cgi-bin/haskell.cgi?Yesod%3aProgrammingEnvironment

cabal install persistent-sqlite
aptitude install libpq-dev libpcre3-dev
cabal install persistent-postgresql

この後適当なディレクトリで yesod init してプロジェクトを生成する。

データベースを作り、config/postgresql.yml を編集する。

yesod init
cd project_dir
vi config/postgresql.yml

依存ライブラリを入れてサーバー起動。

cabal install alex
cabal install
yesod devel

localhost:3000 でサーバーが立ち上がる。

試しに Handler/Root.hs を書き換えるとサーバーがリロードされた。




今日はここまで。

その他参考:

http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/

http://bicycle1885.hatenablog.com/category/Haskell

2010-08-18

[] Haskell + wxWigetsでXRCを使う

ひとまず、

http://0xcc.net/pub/uu-2004-08/

ここの通りにwxgladeを使ってウィンドウを作る。


そしてGenerate codeでXRCを選んで保存。


テストコードを書く。


import Graphics.UI.WX
import Graphics.UI.WXCore

main :: IO ()
main = start win

win :: IO ()
win = do
  f <- frameLoadRes "hello.xrc" "frame_1" []
  menuItemOnCommandRes f "Menu_File_Exit" (close f)
  b <- xmlResourceGetButton f "button_1"
  set b [on command := close f]
  _ <- windowShow f
  return ()

メニューのコマンドはmenuItemOnCommandResで設定するらしい。idに設定した文字列を引数に指定する。

ボタンの方は、nameの値が使われるらしい。CommonタブにId設定欄があるけどXMLでは関係ないみたい。あとEventsタブにハンドラを設定したものはXMLにも反映されているが、汎用的なイベントっぽいので今回は見送り。


これでメニューとボタンは動くが、WXとWXCoreのソースを探索しながらめぼしい関数を試行錯誤しつつ使ってみたのでセオリー通りじゃなかったり間違ってる可能性も。

2010-08-08

[] Webページごとのアクション関数

PHPなどのアクションクラスやアクションメソッドの場合、アクションを実行する関数はRequestsオブジェクトを受け取ってResultsオブジェクトを返すように作っていた。

Haskellの型で書くと

...
data Results = [Result]
type Act = Requests -> Results

のようになる。ここのActはHaskellのIOアクションではなくてWebフレームワークで使われているWeb画面を表示するアクション関数のこと。


これで複数のアクションを実行するには

runActs :: Requests -> [Act] -> Results
runActs req as = concatMap ($ req) as

のような関数を用意して、Actのリストを実行する。


アクションの連結は

-- 例えば、データ登録(a1)した後にデータ表示(a2)するアクションをまとめる
actAct :: Act -> Act -> Act
actAct a1 a2 r = (a1 r) ++ (a2 r)

こうなるのかな。



これで途中まで作っていたけれど、関数型言語だとストリームとかフィルターとかを意識しながら型を考えた方がいいかと思って引数と結果の型を合わせてみる。

data Context = Context { requests :: Requests
                       , results :: Results
                       } deriving Show
type Act = Context -> Context

こうすると、

actAct :: Act -> Act -> Act
actAct a1 a2 = a1 . a2

連結がシンプルになる。




ここで、大抵のActはデータベースやファイルなどのIOを扱うことを考えて

type Act = Context -> IO Context

という型にActを変更する。Requestsに対してパターンマッチをしたいので、受け取る引数はIOを外しておく。

連結は

import Control.Monad ((>=>))

...


actAct :: Act -> Act -> Act
actAct a1 a2 = a1 >=> a2

(>=>)演算子を使う。



フレームワークにはアクション名に対して起動するAct関数を登録する。

type EntryPoint = (String, Act)
type EntryPoints = [(String, Act)]

entryPoints :: EntryPoints
entryPoints = [("Top", top)
              ,("View", view)
              ,("Register", register >=> view)
              ]

top :: Act
top = return . id

view :: Act
view c@(Context { requests = req }) = do
  conn <- connectDb
  res  <- findData conn
  return $ c { results = results c ++ dbToResults res }

register :: Act
register c@(Context { requests = req }) = do
  conn <- connectDb
  res  <- saveData conn req
  let msg = if res then "success" else "error"
  return $ c { results = [Result ("message", msg)] }

今のところこんな感じで作っている。