۲۵ - ۱۴تمرینهای فصل
۱.
rDec
تابعیه که آرگومانش رو در بافت ِ Reader
میگیره و مقداری برمیگردونه که یکی ازش کم شده.
rDec :: Num a => Reader a a
rDec = undefined
Prelude> import Control.Monad.Trans.Reader
Prelude> runReader rDec 1
0
Prelude> fmap (runReader rDec) [1..10]
[0,1,2,3,4,5,6,7,8,9]
دقت کنین که Reader
از transformers
، معادلِ ReaderT
با ساختارِ موندی ِ Identity
ِه. تابعِ runReader
هم برای راحتی، اون ساختار ِ بیمعنی رو دور میندازه. اگه دوست داشتین با runReaderT
یه کم بازی کنین.
۲.
وقتی rDec
کار کرد، خودش و همهی لانداهای داخلیش رو بینقطه کنین (اگه خودتون این کار رو نکرده بودین).
۳.
rShow
همون show
ِه، فقط توی Reader
.
rShow :: Show a
=> ReaderT a Identity String
rShow = undefined
Prelude> runReader rShow 1
"1"
Prelude> fmap (runReader rShow) [1..10]
["1","2","3","4","5","6","7","8","9","10"]
۴.
rShow
هم که کار کرد، بینقطهش کنین.
۵.
rPrintAndInc
اول به همراهِ ورودی سلام میکنه، بعد ورودی به اضافهی ۱ رو خروجی میده.
rPrintAndInc :: (Num a, Show a)
=> ReaderT a IO a
rPrintAndInc = undefined
λ> runReaderT rPrintAndInc 1
Hi: 1
2
λ> traverse (runReaderT rPrintAndInc) [1..10]
Hi: 1
Hi: 2
Hi: 3
Hi: 4
Hi: 5
Hi: 6
Hi: 7
Hi: 8
Hi: 9
Hi: 10
[2,3,4,5,6,7,8,9,10,11]
۶.
sPrintIncAccum
اول به همراهِ ورودی سلام میکنه، بعد ورودی به اضافهی ۱ رو به عنوانِ حالت ِ جدید میذاره، و ورودی ِ اصلی رو به عنوانِ یه String
برمیگردونه.
sPrintIncAccum :: (Num a, Show a)
=> StateT a IO String
sPrintIncAccum = undefined
Prelude> runState sPrintIncAccum 10
Hi: 10
("10",11)
Prelude> mapM (runStateT sPrintIncAccum) [1..5]
Hi: 1
Hi: 2
Hi: 3
Hi: 4
Hi: 5
[("1",2),("2",3),("3",4),("4",5),("5",6)]
تعمیرکاری کُد
این کُد همینطوری تایپچک نمیشه؛ درستش کنین تا بشه. اگه فکر کردین لازمه، import هم اضافه کنین. از توابعی که تا حالا توضیح ندادیم هم استفاده کردیم. نباید تایپها رو تغییر بدین. ممکنه بیشتر از یک جا تعمیرکاری لازم باشه.
import Control.Monad.Trans.Maybe
import Control.Monad
isValid :: String -> Bool
isValid v = '!' `elem` v
maybeExcite :: MaybeT IO String
maybeExcite = do
v <- getLine
guard $ isValid v
return v
doExcite :: IO ()
doExcite = do
putStrLn "say something excite!"
excite <- maybeExcite
case excite of
Nothing -> putStrLn "MOAR EXCITE"
Just e ->
putStrLn ("Good, was very excite: " ++ e)
بازدید شمار
اینجا کلیات یه برنامهی scotty
که تعداد بازدیدها به URIهای بخصوصی رو میشماره بهتون میدیم. این برنامه آرگومانهای command line در اولین اجراش رو به عنوانِ پیشوند ِ کلیدها میذاره.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Data.IORef
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as TL
import System.Environment (getArgs)
import Web.Scotty.Trans
data Config =
Config {
-- یک کلیک و یک!
-- دو کلیک و دو!
-- سه و سه و سه،
-- تا سه نشه، بازی نشه!
counts :: IORef (M.Map Text Integer)
, prefix :: Text
}
به غیر از چیزهایی که از طریقِ IO
دَر میرن، همهی چیزهایی که توی ScottyT
هستن، فقط خوندنی اند، پس نمیشه از StateT
استفاده کنیم.
type Scotty =
ScottyT Text (ReaderT Config IO)
type Handler =
ActionT Text (ReaderT Config IO)
bumpBoomp :: Text
-> M.Map Text Integer
-> (M.Map Text Integer, Integer)
bumpBoomp k m = undefined
app :: Scotty ()
app =
get "/:key" $ do
unprefixed <- param "key"
let key' = mappend undefined unprefixed
newInteger <- undefined
html $
mconcat [ "<h1>Success! Count was: "
, TL.pack $ show newInteger
, "</h1>"
]
main :: IO ()
main = do
[prefixArg] <- getArgs
counter <- newIORef M.empty
let config = undefined
runR = undefined
scottyT 3000 runR app
کُد خرابه و کمبودهایی داره. تو این تمرین باید درستش کنین، با هر چیزی که لازمه.
نهایتاً باید بتونین سروِر رو از داخلِ GHCi اجرا کنین و اینطوری بهش آرگومان بدین:
Prelude> :main lol
Setting phasers to stun...
(port 3000) (ctrl-c to quit)
اگر هم بخواین میتونین یه باینری ِ قابل اجرا بسازین و آرگومانها رو از شِل (م. داخل خودِ ترمینال، خارج از GHCi) بهش بدین، هرطور راحتین. اجرا که شد، باید اینطوری بشه شمارشِش رو زیاد کرد:
$ curl localhost:3000/woot
< h1>Success! Count was: 1</h1>
$ curl localhost:3000/woot
< h1>Success! Count was: 2</h1>
$ curl localhost:3000/blah
< h1>Success! Count was: 1</h1>
دقت کنین که کلید ِ پشتپرده که وقتی /woot
رو GET
میکنین در شمارنده استفاده میشه، در واقع "lolwoot"
ِه، چون به main
آرگومانِ “lol” دادیم. URI ِ یکی از کلیدها رو توی مرورگرِتون هم امتحان کنین و چندبار refresh کنین.
اگه گیر کردین، چندتا مثال، مثل فایلِ reader.hs
در پوشهی مثالهای scotty
از مخزن ِ git ِش رو نگاه کنین.
مورا
۱.
با StateT
و IO
بازیِ مورا* رو بنویسین. امتیاز بازیکن و کامپیوتر میشه حالت. برای شروع، کاری کنین که کامپیوتر تصادفی بازی کنه.
بعد از خروج از بازی، امتیازها رو نشون بدین و به برنده تبریک بگین.
م. مورا یا Morra یه بازی بین دو نفر یا بیشتره. همه همزمان یه عددی رو با انگشتاشون نشون میدن و هرکس عددی که حدس میزنه جمع همهی عددها باشه رو بلند میگه. یه حالت دیگه اینه که دو نفر بازی میکنن و یکی زوج میشه و یکی فرد؛ همزمان با هم یک یا دو انگشت میارن و اگه جمعشون بشه زوج (یعنی ۲ یا ۴)، بازیکن زوج برنده میشه، و اگه جمعشون فرد شه، بازیکن فرد. اینجا منظور حالت دونفره (زوج و فرد)ِ بازیه.
۲.
یه حالت بازیکن با بازیکن هم درست کنین، طوری که بعد از ورودیِ بازیکن اول صفحه خالی بشه تا بازیکنِ دوم ورودیِ بازیکنِ اول رو نبینه.
۳.
کاری کنین کامپیوتر سهتا از حرکتهای بازیکن رو به خاطر بسپاره و هر وقت همون رفتار رو از بازیکن دید، بجای حرکتِ تصادفی، بر حسب عددی که بازیکن انتخاب کرده عددش رو انتخاب کنه. برای مثال:
-- بازیکنه (فرد) P
-- کامپیوتره (زوج) C
P: 1
C: 1
- برندهست C
P: 2
C: 1
- برندهست P
P: 2
C: 1
- برندهست P
در این نقطه، کامپیوتر باید الگو ِ (1, 2, 2) رو ثبت کنه (بازیکن بعد از ۱ و ۲، ۲ انتخاب کرد). دفعهی بعد که بازیکن ۱ و بعد ۲ انتخاب میکنه، کامپیوتر باید فرض کنه که بازیکن ۲ برمیداره، و خودش هم ۲ برداره تا برنده بشه.
۴.
این روشِ ۳-گرم خیلی سادهست. با این حال آدمها خیلی در تصادفی بودن خوب نیستن؛ اکثراً در حرکتهاشون یه جور الگویی دارن.