۲۵ - ۱۴تمرین‌های فصل

۱.

‏‎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)‎‏ رو ثبت کنه (بازیکن بعد از ۱ و ۲، ۲ انتخاب کرد). دفعه‌ی بعد که بازیکن ۱ و بعد ۲ انتخاب می‌کنه، کامپیوتر باید فرض کنه که بازیکن ۲ برمیداره، و خودش هم ۲ برداره تا برنده بشه.

۴.

این روشِ ۳-گرم خیلی ساده‌ست. با این حال آدم‌ها خیلی در تصادفی بودن خوب نیستن؛ اکثراً در حرکت‌هاشون یه جور الگویی دارن.