[英]Haskell numbers and type system?
我有这段Javascript代码:
N1 = Math.floor(275 * month / 9)
N2 = Math.floor((month + 9) / 12)
N3 = (1 + Math.floor((year - 4 * Math.floor(year / 4) + 2) / 3))
N = N1 - (N2 * N3) + day - 30
return N
我试图将其移植到Haskell中。 像这样:
day_of_year year month day = n1 - (n2 * n3) + day - 30
where
n1 = floor(275 * fromIntegral month / 9)
n2 = floor( month + 9 / 12)
n3 = 1 + floor((year - 4 * floor(fromIntegral year / 4) + 2) / 3)
它不起作用:(
这是我的问题:
为什么n1
类型写成n1 :: (Integral b, RealFrac a) => a -> b
但不像n1 :: (RealFrac a, Integral b) => a -> b
它与floor :: (Integral b, RealFrac a) => a -> b
答案: =>左侧的顺序并不重要
ghci通常会尝试使订单与声明中的订单保持一致
但有时它默认为abc排序
这个陈述是否正确: n1
取整数并返回RealFrac。
答:是的。 如果我们知道在=>的左侧排序是不重要的
那么我们也知道(Integral b,RealFrac a)===(RealFrac a,Integral b)
唯一重要的是类型a - > b
或者在这种情况下积分 - > RealFrac
n3
有单态性疾病。 怎么治愈?
我更感兴趣的是大局而不仅仅是让这个工作。 我读过关于单声道...但我不知道放在哪里::在这种情况下:(
答:这里没有单态。 看看FUZxxl的答案:)
day_of_year
可以是这样的: Integral -> Integral -> Integral -> Integral
?
取3个积分并返回积分结果。
答:可以! 它也可以
::积分a => a - > a - > a - > a
:: Int - > Int - > Int - > - > Int
::(积分a,积分a2,积分a1)=> a - > a1 - > a2 - > a2
我想day_of_year
只需要3个Int或3个整数。 它不能像2 Ints 1整数那样混合。 对?
FUZxxl:不,它可以采用不同的参数类型! 看看跟进4 !!!
是否可以创建day_of_year
以获得3个Nums并返回Num?
FUZxxl:是的! 将fromEnum放在年,月,日之前
好的。 每当遇到类型问题时,最好的方法是先向编译器提供显式类型注释。 由于day
, month
和year
可能不是太大,因此将它们设为Int
是个好主意。 你也显然错过了一个支架,我为你修好了:
day_of_year :: Int -> Int -> Int -> Int
day_of_year year month day = n1 - (n2 * n3) + day - 30
where
n1 = floor(275 * fromIntegral month / 9)
n2 = floor((month + 9) / 12)
n3 = 1 + floor((year - 4 * floor(fromIntegral year / 4) + 2) / 3)
当我尝试编译它时,GHC吐出这个相当冗长的错误消息:
bar.hs:8:16: No instance for (RealFrac Int) arising from a use of `floor' Possible fix: add an instance declaration for (RealFrac Int) In the second argument of `(+)', namely `floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)' In the expression: 1 + floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3) In an equation for `n3': n3 = 1 + floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3) bar.hs:8:68: No instance for (Fractional Int) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Int) In the first argument of `floor', namely `((year - 4 * floor (fromIntegral year / 4) + 2) / 3)' In the second argument of `(+)', namely `floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)' In the expression: 1 + floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)
第二个错误是重要的错误,第一个错误是后续问题。 它基本上说: Int
不实现分区而不是floor
。 在Haskell中,积分除法使用不同的函数( div
或quot
),但是你想要浮动除法。 因为year
被固定为一个Int
,所以减数4 * floor(fromIntegral year / 4) + 2
也被固定为Int
。 然后除以3,但如前所述,你不能使用浮动除法。 让我们通过在分割之前将整个术语“转换”为具有fromIntegral
另一种类型(如之前所做的那样)来解决这个问题。
fromIntegral
有签名(Integral a, Num b) => a -> b
。 这意味着: fromIntegral
采用整数类型的变量(例如Int
或Integer
)并返回任何数字类型的变量。
让我们尝试编译更新的代码。 在n2
的定义中出现类似的错误,我也修复了它:
day_of_year :: Int -> Int -> Int -> Int
day_of_year year month day = n1 - (n2 * n3) + day - 30
where
n1 = floor(275 * fromIntegral month / 9)
n2 = floor((fromIntegral month + 9) / 12)
n3 = 1 + floor(fromIntegral (year - 4 * floor(fromIntegral year / 4) + 2) / 3)
此代码编译并运行正常(在我的机器上)。 Haskell具有某些类型默认规则,导致编译器选择Double
作为所有浮动除法的类型。
实际上,你可以做得更好。 如何使用整数除法而不是重复的浮点转换?
day_of_year :: Int -> Int -> Int -> Int
day_of_year year month day = n1 - (n2 * n3) + day - 30
where
n1 = 275 * month `quot` 9
n2 = (month + 9) `quot` 12
n3 = 1 + (year - 4 * (year `quot` 4) + 2) `quot` 3
此算法应始终产生与上述浮点版本相同的结果。 它可能快了大约十倍。 反引号允许我使用函数( quot
)作为运算符。
关于你的第六点:是的,这样做很容易。 只需在year
, month
, day
前面放一个fromEnum
。 函数fromEnum :: Enum a => a -> Int
将任何枚举类型转换为Int
。 Haskell中的所有可用数值类型(复杂的iirc除外)都是Enum
类的成员。 这不是一个好主意,但是你通常会有Int
参数,多余的函数调用可能会减慢你的程序。 明确更好地转换,除非您的函数预期与许多不同类型一起使用。 实际上,不要过分担心微观优化。 ghc有一个复杂的,有点神秘的优化基础设施,使大多数程序快速发展。
是的,你的推理是正确的。
如果你没有给day_of_year
的浮点变量一个类型签名,它的类型默认为day_of_year :: (Integral a, Integral a2, Integral a1) => a -> a1 -> a2 -> a2
。 这实质上意味着: day
, month
和year
可以是实现Integral
类型类的任意类型。 该函数返回与day
相同类型的值。 在这种情况下, a
, a1
和a2
只是不同的类型变量 - 是的,Haskell在类型级别上也有变量(也在类型级别[类型的类型],但这是另一个故事) - 可以满足任何类型。 所以,如果你有
day_of_year (2012 :: Int16) (5 :: Int8) (1 :: Integer)
变量a
被instaniated来Int16
, a1
成为Int8
和a2
变成Integer
。 那么这种情况下的返回类型是什么?
这是
Integer
,看看类型签名!
事实上,你是和不在同一时间。 使类型尽可能通用当然有其优点,但是它会混淆类型检查器,因为当没有显式类型注释的术语中涉及的类型过于笼统时,编译器可能会发现存在多个可能的类型输入一个术语。 这可能会导致编译器通过一些标准化的选择类型,虽然有点不直观的规则,或者它只是用一个奇怪的错误问候你。
如果你真的需要一般类型,那就争取类似的东西
day_of_year :: Integral a => a -> a -> a -> a
也就是说:参数可以是任意的Integral
类型,但所有参数必须具有相同的类型。
永远记住,Haskell 从不投射类型 。 当涉及(自动)铸造时,几乎不可能完全推断出类型。 你只能手动施放。 有些人现在可能会在模块Unsafe.Coerce
告诉你unsafeCoerce
函数,它的类型为a -> b
,但实际上你根本不想知道。 它可能不会做你认为它做的事情。
div
没有任何问题。 当涉及负数时,差异开始出现。 现代处理器(如Intel,AMD和ARM制造的处理器)在硬件中实现quot
和rem
。 div
也使用这些操作,但做了一些小事以获得不同的行为。 当你不真正依赖关于负数的确切行为时,这会不必要地减慢计算速度。 (有实现实际上是几台机器div
而不是quot
在硬件,唯一一个我现在还记得是MMIX虽然)
我有太多的后续问题需要简单的评论。
n1
很明显。 fromIntegral
需要month
并将其转换为/
某种类型的nesesary。
我在这儿吗?
是。
但在n2
我们可以假设
fromIntegral(month + 9)
=== (fromIntegral month + 9)
month
和9
,然后将其转换为某种类型的/
这是因为+
是在Num
,所以每个数字都可以是+
而不是强制转换。 像1,2,3
这样的原始数字也是Num
类型。 (fromIntegral month + 9)
有类型Num a => a
但是由于/12
编译器转换month
和9
到某些类型与/
兼容。 ! 是。
FUZxxl,伙计,谢谢!
我非常接近于通过修补代码并随意放置fromIntegral
来解决这个问题。
但是让代码工作与知道我们为什么要做某事并不相同!
floor
AND /
不允许使用Integral
! n3
,第二year
变量:通过使用floor(fromIntegral year / 4)
来自整floor(fromIntegral year / 4)
我真实地做出了floor
可以使用的结果。 那个表达完整 (year - 4 * floor(fromIntegral year / 4) + 2)
从Integral
(year - 4 * floor(fromIntegral year / 4) + 2)
Integral
类别! /3
和floor
无法工作。 ! 是。
您的输入有效: day_of_year :: Int -> Int -> Int -> Int
我的输入也有效: day_of_year :: Integral a => a -> a -> a -> a
自动输入: day_of_year :: (Integral a, Integral a2, Integral a1) => a -> a1 -> a2 -> a2
这是什么意思? 什么是a1,a2? 为什么a,a1,a2?
后续行动4中的答案很棒
我在这里试图创建一个使用Integral
而不是特定的Int
或Integer
通用函数?
Number
。 ! 看看后续行动5
你为什么用quot
而不是div
?
昨天我完全尝试了你用div
做的事情, ghci
奖励我:
No instance for (Integral (Car -> Int)) arising from a use of div
! 我不小心从功能定义中删除了一年和一个月
! day_of_year day = ...
! 那个错误出现了。
ps Google: 找不到haskell的结果“(Integral(Car - > Int))”
即使谷歌也找不到我的错别字;)))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.