【问题标题】:Is there a better way of doing this in Haskell?在 Haskell 中有没有更好的方法来做到这一点?
【发布时间】:2014-09-05 04:59:02
【问题描述】:

我写了以下内容来帮助大孩子完成他们的家庭教育工作,并通过学习如何编程来保持头脑工作(我认为 haskell 听起来很棒)。

main :: IO ()
main = do
   putStrLn "Please enter the dividend :"
   inputx  <- getLine
   putStrLn "Please enter the divisor :"
   inputy  <- getLine
   let x = (read inputx) :: Int
   let y = (read inputy) :: Int
   let z = x `div` y
   let remain = x `mod` y
   putStrLn ( "Result: " ++ show x ++ " / " ++ show y ++ " =  " ++ show z ++  " remainder " ++ show remain )

   putStrLn ( "Proof: (" ++ show y ++ " x " ++ show z ++ ") = " ++ show (y * z) ++ " + " ++ show remain ++ " = " ++ show ((y * z) + remain))
   putStrLn ( "Is this what you had? ")

他们这样做是不是更整洁/更好/更好/更紧凑的方式?

【问题讨论】:

  • 我认为这很好 - 你可以缩短一些部分(例如 merge readgetLine - 使用 &lt;$&gt;div/ mod 使用divMod - 不过我觉得这么简单的程序就可以了
  • 风格主要是一种偏好,但我会做以下事情:input &lt;- putStrLn "prompt" &gt;&gt; getLine;顺序的 let 语句不需要在每一行上都有一个 let(即用 3 个空格替换第一个之后的所有 let),而不是按顺序排列 putStrLn,我会写 putStrLn $ "line 1" ++ "line 2" ++ "line 3"(每个字符串可以在自己的行上) - 只要以下行缩进,haskell 就知道它在同一语句中的部分)。
  • @user2407038 你的putStrLn 会漏掉一些换行符 ;)
  • 既然你没有验证输入(看看它是否真的可以被解析为字符串),你不妨直接使用 readLn 而不是 getLine。
  • 您可能会在codereview.stackexchange.com 上得到更全面的回复,这是一个专门用于代码审查的网站。

标签: haskell integer-arithmetic


【解决方案1】:

这将受益于一个关键原则:尽可能将纯代码与 IO 分开。这将使您的程序扩大规模并保持main breif。在一个大的main 中包含大量let 并不是一种非常实用的方法,并且随着代码的增长往往会变得更加混乱。

使用类型签名和readLn(本质上是fmap read getLine)有助于减少一些麻烦。 (如果您不熟悉fmap,请访问问题How do functors work in haskell?fmap 确实是一个非常灵活的工具。)

getInts :: IO (Int, Int)
getInts = do
    putStrLn "Please enter the dividend :" 
    x <- readLn 
    putStrLn " Please enter the divisor :"
    y <- readLn
    return (x,y)

现在正在处理。如果我用这种数据做更多的事情,或者更频繁地,我会使用记录类型来存储被除数、除数、商和余数,因此请记住这一点,以备将来使用,但这在这里有点过头了。

我返回的是一个列表而不是一个元组,所以我可以使用 mapshow 全部:

sums :: (Int, Int) -> [Int]
sums (x,y) = [x, y, q, r, y * q, y * q + r] where
            q = x `div` y
            r = x `mod` y

拼图的最后一块是输出。同样,我更喜欢在外部生成这个 IO,然后我可以稍后在其上使用mapM_ putStrLn 来打印每一行。我更喜欢这个记录类型,但我容忍一个字符串列表作为输入,因为我假设我已经shown 他们全部了。

explain :: [String] -> [String]
explain [x,y,q,r,yq,yq_r] = 
  [ concat ["Result: ", x, " / ", y, " =  ", q, " remainder ", r]
  , concat ["Proof: (", y, " x ", q, ") + ", r, " = ", yq, " + ", r, " = ", yq_r]
  , "Is this what you had? "]

现在我们可以把main写成

main = do (x,y) <- getInts
          let ns = map show ( sums (x,y) )
              es = explain ns
          mapM_ putStrLn es

或者更简洁,通过将函数explain . map show . sums 连接在一起,然后使用fmap 将其应用于getInts 的输出:

main :: IO ()
main = fmap (explain . map show . sums) getInts
       >>= mapM_ putStrLn

您可能会注意到我在证明中添加了一个+r 以使= 始终表示=,这是正确的数学用法,以及mirror 的Haskell 对= 的含义。

【讨论】:

  • 感谢@WillNess,您的编辑总是很好,并且总是欢迎。
  • 谢谢,安德鲁,那我不再问了。 :) :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
相关资源
最近更新 更多