我有这段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 - >一个
:: 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放在年,月,日前面
答案 0 :(得分:16)
好。每当遇到类型问题时,最好的方法是先向编译器提供显式类型注释。由于day
,month
和year
可能不是太大,因此最好将它们Int
设为{1}}。你也显然错过了一个支架,我为你解决了这个问题:
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
)作为运算符。
关于你的第六点:是的,这样做很容易。只需将fromEnum
放在year
,month
和day
之前。函数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
变为Int16
,a1
变为Int8
,a2
变为Integer
。那么这种情况下的返回类型是什么?
它是
Integer
,看一下类型签名!
事实上,你现在和不在同一时间。使类型尽可能通用当然有其优点,但是它会混淆类型检查器,因为当没有显式类型注释的术语中涉及的类型过于笼统时,编译器可能会发现存在多个可能的类型输入一个术语。这可能会导致编译器通过一些标准化的选择类型,虽然有点不直观的规则,或者只是用一个奇怪的错误问候你。
如果你真的需要一般类型,请努力寻找像
这样的东西day_of_year :: Integral a => a -> a -> a -> a
即:参数可以是任意Integral
类型,但所有参数必须具有相同的类型。
永远记住Haskell 永远不会转换类型。当涉及(自动)铸造时,几乎不可能完全推断出类型。你只能手动施放。有些人现在可能会告诉您模块unsafeCoerce
中的函数Unsafe.Coerce
,其类型为a -> b
,但您实际上并不想知道。它可能不会做你认为它做的事情。
div
没有错。当涉及负数时,差异开始出现。现代处理器(如Intel,AMD和ARM制造的处理器)在硬件中实现quot
和rem
。 div
也使用这些操作,但做了一些小事以获得不同的行为。当你不真正依赖关于负数的确切行为时,这会不必要地减慢计算速度。 (实际上有一些机器在硬件中实现div
但不实现quot
。我现在唯一能记住的是mmix但是
答案 1 :(得分:0)
对于一个简单的评论,我有太多的跟进问题。
n1
很明显。 fromIntegral
需要month
并将其转换为/
的某种类型的必需品
我在这儿吗?
是的。
但在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
不允许/
和Integral
! n3
,第二个year
变量:
使用floor(fromIntegral year / 4)
我真实地做出了floor
可以使用的结果。
而那个表达完整了
(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 = ...
!并且出现了错误。
P.S。 Google:没有找到针对haskell的结果“(Integral(Car - > Int))”
即使谷歌找不到我的拼写错误;)))