Haskell 数字和类型系统?

2023-12-27

我有这段 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)

它不起作用:(
这是我的问题:

  1. Why is n1类型写成n1 :: (Integral b, RealFrac a) => a -> b
    但不喜欢n1 :: (RealFrac a, Integral b) => a -> b
    floor :: (Integral b, RealFrac a) => a -> b

    Answer:左侧的顺序并不重要=>
    ghci 一般会尽量保持与声明中的顺序相同
    但有时它默认为 abc 排序

  2. 这个说法是否正确:n1获取整数并返回 RealFrac。

    Answer:是的。如果我们知道左侧的排序并不重要=>
    那么我们还知道 (Integral b, RealFrac a) === (RealFrac a, Integral b)
    唯一重要的是类型一个 -> 乙
    或者在这种情况下积分 -> RealFrac

  3. n3有单态病。怎样才能治愈呢?
    我对大局更感兴趣,而不仅仅是让这个功能发挥作用。我读过有关单声道的内容...但我不知道在这种情况下将 :: 放在哪里:(

    Answer:这里没有单态性。看看 FUZxxl 的回答:)

  4. Can day_of_year像这样:Integral -> Integral -> Integral -> Integral?
    进行 3 次积分并返回积分结果。

    Answer:是的,它可以!也可以是
    :: 积分 a => a -> a -> a -> a
    :: 整数 -> 整数 -> 整数 -> -> 整数
    :: (积分 a, 积分 a2, 积分 a1) => a -> a1 -> a2 -> a2

  5. 我想day_of_year只能采用 3 个 Ints 或 3 个 Integers。它不能像 2 Ints 1 整数那样混合。正确的?

    FUZxxl:不,它可以混合使用不同的参数类型!请看后续4!!!

  6. 是否可以创建day_of_year获取 3 个数字并返回一个数字?

    FUZxxl:是的!在年、月、日前面放置一个 fromEnum


好的。每当遇到类型问题时,最好的方法是向编译器提供显式类型注释。自从day, month and year可能不太大,制作它们是个好主意Ints。你显然还错过了一个大括号,我为你修复了这个问题:

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 or quot),但你想要浮动除法。自从year被固定为Int, 减数4 * floor(fromIntegral year / 4) + 2也被固定为Int。然后除以 3,但如前所述,不能使用浮动除法。让我们通过将整个术语“转换”为另一种类型来解决这个问题fromIntegral在划分之前(就像你之前所做的那样)。

fromIntegral有签名(Integral a, Num b) => a -> b。这意味着:fromIntegral接受一个整型变量(例如Int or 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 and day。功能fromEnum :: Enum a => a -> Int将任何枚举类型转换为Int。 Haskell 中所有可用的数字类型(除了复杂的 iirc)都是该类的成员Enum。但这不是一个好主意,因为你通常有Int参数和多余的函数调用会减慢程序的速度。最好显式转换,除非您的函数预计与许多不同类型一起使用。实际上,不必太担心微观优化。 ghc 有一个复杂且有些神秘的优化基础架构,可以使大多数程序运行得非常快。

修正案

后续1、2和3

是的,你的推理基本正确。

后续4

如果你不给出浮点变体day_of_year类型签名,其类型默认为day_of_year :: (Integral a, Integral a2, Integral a1) => a -> a1 -> a2 -> a2。这本质上意味着:day, month and year可以是实现以下功能的任意类型Integral类型类。该函数返回与以下类型相同的值day。在这种情况下,a, a1 and a2只是不同类型变量- 是的,Haskell 也有类型级别的变量(以及类型级别 [这是类型的类型],但那是另一个故事) - 可以满足任何类型。所以如果你有

day_of_year (2012 :: Int16) (5 :: Int8) (1 :: Integer)

变量a被实例化为Int16, a1变成Int8 and a2变成Integer。那么这种情况下的返回类型是什么?

It's Integer,看看类型签名!

后续5

事实上,你同时存在又不同时。使类型尽可能通用当然有其优点,但它会使类型检查器感到困惑,因为当没有显式类型注释的术语中涉及的类型太通用时,编译器可能会发现有不止一种可能键入一个术语。这可能会导致编译器通过一些标准化但有些不直观的规则来选择类型,或者它只是向您显示一个奇怪的错误。

如果您确实需要通用类型,请争取类似的东西

day_of_year :: Integral a => a -> a -> a -> a

也就是说:参数可能是任意的Integral类型,但所有参数必须具有相同的类型。

永远记住哈斯克尔从不强制类型转换。当涉及(自动)转换时,几乎不可能完全推断类型。您只能手动投射。现在有些人可能会告诉您该功能unsafeCoerce在模块中Unsafe.Coerce,其类型为a -> b,但你实际上不想知道。它可能不会做你想象的那样。

后续6

没有什么问题div。当涉及负数时,差异开始出现。现代处理器(如 Intel、AMD 和 ARM 制造的处理器)实现quot and rem在硬件方面。div也使用这些操作,但做了一些调整以获得不同的行为。当您并不真正依赖于负数的确切行为时,这会不必要地减慢计算速度。 (实际上有一些机器实现了div但不是quot在硬件方面。我现在唯一记得的是mmix /questions/tagged/mmix though)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell 数字和类型系统? 的相关文章

  • TypeScript:实现具有调用签名和索引签名的接口

    我想创建一个满足此类型的对象 interface I string x string number 并通过 TypeScript 类型检查 理想情况下 我希望不需要诉诸技巧 例如使用any作为中间步骤 我知道可以将其他字段添加到具有调用签名
  • 获取参数类型的参数

    假设我定义了一个这样的类型 type Point Tx Ty end 然后我创建一个这种类型的变量 例如 a Point Int64 something 现在 我只知道我可以获得以下类型a by typeof a 那是 Point Int6
  • Haskell:是的,没有类型类。为什么是整数?

    我有一个关于 GHCi 如何假定整数类型的问题 我正在阅读 Learn you a Haskell 是 否类型的课程 如果您想阅读全文 这里有一个链接 http learnyouahaskell com making our own typ
  • 将两个 Int 值相除以获得 Float 的正确方法是什么?

    我想分两份IntHaskell 中的值并获得结果Float 我尝试这样做 foo Int gt Int gt Float foo a b fromRational a b 但 GHC 版本 6 12 1 告诉我 无法将预期类型 Intege
  • Haskell - 用防护罩替换外壳

    我想知道在这部分代码中是否可以用守卫替换 case 语句 firstFunction String gt Maybe MyType secondFunction MyType gt Integer myFunction String gt
  • 使用 FoldLine 解析多个块

    对于这个简化的问题 我试图解析一个如下所示的输入 foo bar baz quux woo hoo xyzzy glulx into foo bar baz quux woo hoo xyzzy glulx 我尝试过的代码如下 import
  • Haskell 中列表列表的笛卡尔积

    给定一个长度列表的列表x所有子列表的长度都相同y 输出y x长度列表x包含每个子列表中的一项 例子 x 3 y 2 1 2 3 4 5 6 Output 2 3 8不同的输出 1 3 5 1 4 5 1 3 6 1 4 6 2 3 5 2
  • Powershell日期类型无法找到

    我正在尝试使用PowerShell连接virustotal API 代码来自virustotal网站 我得到 无法找到类型 System Security Cryptography ProtectedData 错误信息 代码如下 funct
  • 在 Yesod 生态系统中,对某些文本进行 urlencode 的最佳方式是什么?

    我想对一些文本进行 url 编码 例如 用 20 替换每个空格等 我找到了 HTTP Network HTTP Base urlEncode 并且可以使用它 但我想知道是否还有其他通常在 Yesod 生态系统中使用的东西 不幸的是 由于 U
  • 标准的能力

    我发现了一些使用标准的旧例子here http www serpentine com blog 2009 09 29 criterion a new benchmarking library for haskell 看起来好像早在 2009
  • Haskell / GHC - 是否有“警告不完整模式”的中缀标签/编译指示

    我正在寻找一个可以对特定的不完整模式发出警告的编译指示 它会使编译器失败并显示以下 假设的 代码 FAILIF incomplete patterns f Int gt Int f 0 0 我正在尝试使用 Arrows 编写一个 编译器 并
  • Haskell 中的尾递归字符串分割

    我正在考虑分割字符串的问题s在一个字符处c 这表示为 break c s 其中 Haskell 库定义break c 足够接近 br br s h t if c h then s else let h t br t in h h t 假设我
  • Haskell Stack 从 github 安装包依赖项

    是否可以使用 Haskell 堆栈从 github 安装软件包的版本 例如在一个 cabal or a stack yaml文件 如何在 git repo branch revision 上指向依赖项 对于堆栈 The 的文档stack y
  • 如何在haskell中获取变量名称

    我来到 haskell 时有一些 c 背景知识 想知道是否有类似的 define print a printf s d n a a int a 5 print a 应该打印 a 5 这是 augustss 提到的 TH 解决方案 LANGU
  • 这个对自身单位的列表理解是如何工作的?

    在 haskell IRC 频道中有人问 是否有一种简洁的方法来定义一个列表 其中第 n 个条目是之前所有条目的平方和 我认为这听起来像一个有趣的谜题 递归定义无限列表是我真正需要练习的事情之一 所以我启动了 GHCi 并开始尝试递归定义
  • 为什么我在 MySQL 中设置更大的 INT 数据类型长度时没有收到错误消息?

    我对 MySql 中的数据类型长度有点困惑 我阅读了参考手册http dev mysql com doc refman 5 0 en data types html http dev mysql com doc refman 5 0 en
  • 带有 RankNTypes 扩展的奇怪类型推断

    我正在尝试在 Haskell 中尝试 System F 类型 并通过以下方式实现了自然数的 Church 编码type 当加载这段代码时 OPTIONS GHC Wall LANGUAGE RankNTypes type CNat fora
  • 过滤器的 Scala 集合类型

    假设您有一个 List 1 1 其类型为 List Any 这当然是正确的且符合预期 现在如果我像这样映射列表 scala gt List 1 1 map case x Int gt x case y String gt y toInt 结
  • 如何在Haskell中实现词法分析器和解析器

    我在这里得到了这段代码 它是用Haskell结构的命令式编程语言编写的程序 所以问题是 我如何为这种语言实现词法分析器和解析器 该程序被定义为一系列语句有 6 种类型 goto write stop if goto 和 int int n
  • 如何打乱列表?

    如何从一组数字 1 2 3 直到我击中x 我的计划是重新调整列表 1 2 3 并把它砍在x chopAt 3 2 3 1 2 3 chopAt 3 2 1 3 2 1 3 chopAt 3 3 1 2 3 chopAt chopAt x y

随机推荐