Haskell:签名/类型混乱的简单计算

use*_*478 2 haskell

我对Haskell非常新,并试图学习这头野兽.这是一个简单的函数,可以将时间(以秒为单位)转换为[天,小时,分钟,秒].我已经用签名和类型挣扎了半天,但我仍然遇到类型错误.看起来我已经尝试过各种签名组合 - 没有运气.能否请你帮忙.

  1. 我需要什么签名?我尝试了secToTime :: Integer -> [Integer]第一个并且得到了一堆类型错误,这些错误足以让我迷惑不解.
  2. 是显示结果列表的正确方法:main = do print secToTimemain = do map (\x -> putStrLn . show) secToTime

    secToTime secs = [d,h,m,s]
      where s = secs `rem` 60
            m = truncate(secs / 60) `rem` 60
            h = truncate(secs / 3600) `rem` 24
            d = truncate(secs / 86400)
    
    Run Code Online (Sandbox Code Playgroud)

谢谢

Tik*_*vis 9

问题在于你正在混合rem,它与Integrals一起使用并且/Fractionals一起使用.并且易于修复将用于div代替/.这也意味着truncate不需要因为div表示整数除法.

基本上,Haskell将类似整数的类型与其他类型区分开来.类型类中的任何Integral类型都像一个整数 - 这些包括Int,Integer以及大量更专业的类型Word.对于您的特定代码,您希望将所有数字视为整数,因此您应该使用类似的类型的函数Integral a => a -> a -> a,div而不是/.

另请注意:由于您的时间表示总是有四个字段,因此不应使用列表.您可以改为使用元组(d,h,m,s)或创建自己的类型:

data Time = Time Integer Integer Integer Integer deriving (Show, Eq)
Run Code Online (Sandbox Code Playgroud)

这个deriving (Show, Eq)位只是给你show和免费==Time类型.

所以,把所有这些放在一起,我们得到这个功能:

secToTime secs = Time d h m s
  where s = secs `rem` 60
        m = secs `div` 60 `rem` 60
        h = secs `div` 3600 `rem` 24
        d = secs `div` 86400
Run Code Online (Sandbox Code Playgroud)

所以现在自然的问题是,这有什么类型的签名?它实际上非常简单:

secToTime :: Integer -> Time
Run Code Online (Sandbox Code Playgroud)

请注意这是如何使返回数据代表的非常明显!

由于Time派生Show,我们可以使用print它.所以我们可以:

main = print $ secToTime 12345
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为在内部print只调用show后输出结果.由于Time是一个实例Show,它有一个show定义的函数,因此可以使用print.

运行此程序将打印:

Time 0 3 25 45
Run Code Online (Sandbox Code Playgroud)

但是,这不是一个理想的结果!它打印的格式很尴尬.相反,让我们首先摆脱派生Show:

data Time = Time Integer Integer Integer Integer deriving (Eq)
Run Code Online (Sandbox Code Playgroud)

并写我们自己的.有趣!

instance Show Time where 
  show (Time d h m s) = show d ++ " days " ++ 
                        show h ++ " hours " ++ 
                        show m ++ " minutes " ++ 
                        show s ++ " seconds"
Run Code Online (Sandbox Code Playgroud)

现在,当我们运行时main,我们得到这个输出:

0 days 3 hours 25 minutes 45 seconds
Run Code Online (Sandbox Code Playgroud)

它好多了.基本上,我们所做的就是通过指定实例和定义函数告诉Haskell Times应该如何看作Strings .Showshow

鉴于您有一个Time类型,您可以使用模式匹配从中提取值:

timeToSec (Time d h m s) = d * 86400 + h * 3600 + m * 60 + s
Run Code Online (Sandbox Code Playgroud)

该模式(Time d h m s)需要你的Time价值和解构它放回它包含四个整数,命名它们d,h,ms.这使您可以Time通过获取实际数字来编写可以在s上运行的任意函数.