۲۵ - ۱۴تمرینهای فصل
۱.
rDec تابعیه که آرگومانش رو در بافت ِ Reader میگیره و مقداری برمیگردونه که یکی ازش کم شده.
rDec :: Num a => Reader a a
rDec = undefinedPrelude> 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 = undefinedPrelude> 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 = undefinedPrelude> 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) رو ثبت کنه (بازیکن بعد از ۱ و ۲، ۲ انتخاب کرد). دفعهی بعد که بازیکن ۱ و بعد ۲ انتخاب میکنه، کامپیوتر باید فرض کنه که بازیکن ۲ برمیداره، و خودش هم ۲ برداره تا برنده بشه.
۴.
این روشِ ۳-گرم خیلی سادهست. با این حال آدمها خیلی در تصادفی بودن خوب نیستن؛ اکثراً در حرکتهاشون یه جور الگویی دارن.