Haskell:简单计算中的签名/类型混乱
我对哈斯凯尔很陌生,我正在努力学习野兽。 下面是一个简单的函数,它将时间(以秒为单位)转换为[天、小时、分钟、秒]。 我花了半天时间在签名和类型上苦苦挣扎,但仍然出现了一个类型错误。 看起来我已经尝试了所有的签名组合-没有运气。你能帮忙吗Haskell:简单计算中的签名/类型混乱,haskell,Haskell,我对哈斯凯尔很陌生,我正在努力学习野兽。 下面是一个简单的函数,它将时间(以秒为单位)转换为[天、小时、分钟、秒]。 我花了半天时间在签名和类型上苦苦挣扎,但仍然出现了一个类型错误。 看起来我已经尝试了所有的签名组合-没有运气。你能帮忙吗 我需要什么签名?我首先尝试了secToTime::Integer->[Integer],得到了一大堆类型错误,这些错误非常隐晦,足以把我搞糊涂了 显示结果列表的正确方法是:main=do print secToTime还是main=do map(\x->put
secToTime::Integer->[Integer]
,得到了一大堆类型错误,这些错误非常隐晦,足以把我搞糊涂了main=do print secToTime
还是main=do map(\x->putstrn.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)
谢谢问题是您正在混合使用
rem
,它与整数
s一起工作,与分数
s一起工作。简单的解决方法是使用div
代替/
。这也意味着不需要truncate
,因为div
表示整数除法
基本上,Haskell将类似整数的类型与其他类型区分开来。Integral
typeclass中的任何类型都像一个整数——包括Int
、integer
和大量更专门的类型,如Word
。对于您的特定代码,您希望将所有数字视为整数,因此应该使用类型为Integral a=>a->a->a
的函数,例如div
,而不是/
另一个注意事项:由于时间表示法始终有四个字段,因此不应使用列表。您可以使用元组(d、h、m、s)
或创建自己的类型:
data Time = Time Integer Integer Integer Integer deriving (Show, Eq)
派生(Show,Eq)
位只为您免费提供Show
和=
时间类型
所以,把所有这些放在一起,我们得到了这个函数:
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
所以现在自然的问题是,这有什么类型的签名?其实很简单:
secToTime :: Integer -> Time
请注意,这使得返回的数据所代表的内容非常明显
由于时间
派生显示
,我们只需在其上使用打印
。因此,我们可以:
main = print $ secToTime 12345
这是因为在内部,print
只调用show
,然后输出结果。由于Time
是Show
的一个实例,它定义了一个Show
函数,因此可以与print
一起使用
运行此程序将打印:
Time 0 3 25 45
然而,这并不是一个理想的结果!打印出来的格式很难看。相反,让我们先去掉派生的Show
:
data Time = Time Integer Integer Integer Integer deriving (Eq)
写我们自己的。有趣
instance Show Time where
show (Time d h m s) = show d ++ " days " ++
show h ++ " hours " ++
show m ++ " minutes " ++
show s ++ " seconds"
现在,当我们运行main
时,我们得到以下输出:
0 days 3 hours 25 minutes 45 seconds
好多了。基本上,我们所做的就是通过指定Show
实例并定义Show
函数,告诉HaskellTime
s应该如何显示为String
s
假设您有一个Time
类型,您可以使用模式匹配从中提取值:
timeToSec (Time d h m s) = d * 86400 + h * 3600 + m * 60 + s
模式(Time d h m s)
获取您的Time
值,并将其解构为包含的四个整数,分别命名为d
、h
、m
和s
。这使您可以编写任意函数,通过输出实际数字,这些函数可以在时间
上运行。就打印而言
main = print $ secToTime 1234
应该有效(因为只有一个语句,所以不需要do)。请注意,您要么需要一个$
放在我放置它的位置,要么需要将secToTime
及其参数用括号括起来print secToTime
本身就是一个类型错误,因为无法打印函数,print secToTime 1234
是双重类型错误-首先,Haskell将print
应用于secToTime
,产生如上所述的类型错误,然后(假装可以打印secToTime
),Haskell将应用调用print
得到的IO()
,并将其应用于1234
,这完全是胡说八道,因为IO()
不是函数
括号使secToTime
首先应用于1234
,然后结果列表将传递给print
。$
基本上做了相同的事情-它是一个普通函数,接受其右参数并将其传递给左参数。由于运算符绑定的函数低于正常函数,因此首先应用了
secToTime,使所有函数都能正常工作。虽然我完全同意Tikhon的解决方案最适合这种特殊情况,但让我向您展示如何以一般方式从秒生成列表:
secToTime secs = let mods = [24,60,60]
(d:rest) = scanr (flip div) secs mods
in d: zipWith mod rest mods
非常感谢你!啊,那些小东西。。。我喜欢这种新型的建议——有利于学习。但我似乎无法从main打印它。它抱怨“没有show的实例(show(Integer->Time)),我只做了一个简单的操作:“main=print secToTime”“再次感谢。很抱歉,在看到您关于如何显示的答案之前,我发布了后续问题。创建Show Time的实例很好,但稍后我可能希望以不同的方式格式化输出。如果我想把格式留到以后呢?如何从时间类型中提取整数以进行进一步格式化/处理?作为参考,
print secToTime
尝试打印函数secToTime
。要实际打印时间值,需要提供secToTime
参数。我将通过编辑答案来解决您后面的问题。我建议使用一个带有命名字段的类型,data Time=Time{days,hours,minutes,seconds::Integer}
。看起来非常。。。不同:)我正处于这样一个学习阶段,无法完全理解/欣赏上述内容。虽然我对学习很感兴趣