如何在 Haskell 中实现二进制数

2023-12-26

我看到了以下教堂数字的数据构造函数

data Nat = Zero | Succ Nat deriving Show

但这是一元数。 我们如何以这种方式在 Haskell 中实现二进制数的数据构造函数?

我已经尝试过这个:

data Bin = Zero | One | BinC [Bin] deriving Show

之后我们可以得到,十进制5编码为BinC [One,Zero,One]

但我想我在这里遗漏了一些东西。我的解决方案似乎不如教会的解决方案聪明。毫不奇怪,我不是教会。稍微思考一下,我发现我的解决方案依赖于列表,而 Nat 不依赖于任何此类外部结构(如列表)。

我们是否可以编写一个类似于 Church 的解决方案,也使用 Succ 类型构造函数来处理二进制数?如果是,怎么办?我尝试了很多,但似乎我的大脑无法摆脱列表或其他类似结构的需要。


我能想到的最接近的是

λ> data Bin = LSB | Zero Bin | One Bin
λ|  -- deriving Show

这使得构造二进制数成为可能

λ> One . One . Zero . Zero . One . One $ LSB
One (One (Zero (Zero (One (One LSB)))))

人们还可以想象一个解码函数的工作原理是(Ingo 在评论中建议的更好的版本)

λ> let toInt :: (Integral a) => Bin -> a
λ|     toInt = flip decode 0
λ|       where decode :: (Integral a) => Bin -> a -> a
λ|             decode LSB value = value
λ|             decode (Zero rest) value = decode rest (2*value)
λ|             decode (One rest) value = decode rest (2*value + 1)

然后可用于将二进制数解码为整数。

λ> toInt (Zero . One . One . One . Zero . Zero . One $ LSB)
57

您想要完成的任务的困难在于您需要“从内到外”读取二进制数,或者可以这么说。要知道最高有效数字的值,您需要知道该数字有多少位。如果您要以“反向”方式编写二进制数 - 即最外面的数字是最低有效数字,那么事情会更容易处理,但当您创建它们并使用默认实例打印它们时,数字会向后看的Show.

对于一元数字来说,这不是问题,因为没有“最低有效数字”,因为所有数字都具有相同的值,因此您可以从任一方向解析数字,并且会得到相同的结果。


为了完整起见,这里是相同的事情,但最外面的数字是最低有效数字:

λ> data Bin = MSB | Zero Bin | One Bin
λ|   -- deriving Show

这看起来和以前很像,但是你会注意到,当实现解码函数时,

λ> let toInt = flip decode (1,0)
λ|       where
λ|         decode (One rest) (pos, val) = decode rest (pos*2, val+pos)
λ|         decode (Zero rest) (pos, val) = decode rest (pos*2, val)
λ|         decode MSB (_, val) = val

数字是倒着写的!

λ> toInt (Zero . Zero . Zero . One . Zero . One $ MSB)
40

然而,这更容易处理。例如,我们可以根据具体情况将两个二进制数相加。 (警告:大量案例!)

λ> let add a b = addWithCarry a b False
λ|      where
λ|        addWithCarry :: Bin -> Bin -> Bool -> Bin
λ|        addWithCarry MSB MSB True = One MSB
λ|        addWithCarry MSB MSB False = MSB
λ|        addWithCarry MSB b c = addWithCarry (Zero MSB) b c
λ|        addWithCarry a MSB c = addWithCarry a (Zero MSB) c
λ|        addWithCarry (Zero restA) (Zero restB) False = Zero (addWithCarry restA restB False)
λ|        addWithCarry (One restA)  (Zero restB) False = One (addWithCarry restA restB False)
λ|        addWithCarry (Zero restA) (One restB)  False = One (addWithCarry restA restB False)
λ|        addWithCarry (One restA)  (One restB)  False = Zero (addWithCarry restA restB True)
λ|        addWithCarry (Zero restA) (Zero restB) True = One (addWithCarry restA restB False)
λ|        addWithCarry (One restA)  (Zero restB) True = Zero (addWithCarry restA restB True)
λ|        addWithCarry (Zero restA) (One restB)  True = Zero (addWithCarry restA restB True)
λ|        addWithCarry (One restA)  (One restB)  True = One (addWithCarry restA restB True)

此时添加两个二进制数是轻而易举的事:

λ> let forty = Zero . Zero . Zero . One . Zero . One $ MSB
λ|     eight = Zero . Zero . Zero . One $ MSB
λ|
λ> add forty eight
Zero (Zero (Zero (Zero (One (One MSB)))))

确实如此!

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

如何在 Haskell 中实现二进制数 的相关文章

  • Haskell 中的相互递归求值器

    Update 我已经添加一个答案 https stackoverflow com questions 3524485 mutually recursive evaluator in haskell 4504200 4504200这描述了我的
  • 如何组合过滤条件

    过滤器类函数接受一个条件 a gt Bool 并在过滤时应用它 当您有多个条件时 使用过滤器的最佳方法是什么 使用了应用函数 liftA2 而不是 liftM2 因为出于某种原因我不明白 liftM2 在纯代码中如何工作 liftM2 组合
  • 反应性香蕉时间延迟

    我已经查阅了文档反应香蕉 http hackage haskell org package reactive banana 而且我找不到指定明确时间延迟的方法 举例来说 我想采取Event t a并将其所有发生的事件移至未来 1 秒 或获取
  • Haskell 错误处理方法

    毫无疑问 Haskell 中有多种机制来处理错误并正确处理它们 错误单子 要么 也许 异常等 那么为什么用其他语言编写容易出现异常的代码比用 Haskell 感觉更简单呢 假设我想编写一个命令行工具来处理命令行上传递的文件 我想 验证提供的
  • 解析 PHOAS 表达式

    我想我理解 PHOAS 参数化高阶抽象语法 我明白了如何漂亮地打印一个表达式 参见http www reddit com r haskell comments 1mo59h phoas for free by edward kmett cc
  • Spring.NET 和构造函数拦截器

    我正在尝试在构造时对对象进行一些 AOP 并找到了 IConstructorInterceptor 这对于我想要的东西来说是完美的 但它似乎不起作用 http jira springframework org browse SPRNET 2
  • Haskell 和 Idris 之间的区别:类型宇宙中运行时/编译时的反映

    因此 在 Idris 中 编写以下内容是完全有效的 item b Bool gt if b then Nat else List Nat item True 42 item False 1 2 3 cf https www youtube
  • Haskell 排列库函数 - 请澄清一下?

    这是代码permutationsHaskell 中的函数Data List module permutations a gt a permutations xs0 xs0 perms xs0 where perms perms t ts i
  • 简单的秒差距示例会产生类型错误

    我正在尝试编译这个简单的秒差距代码 import Text Parsec simple letter 但我不断收到此错误 No instance for Stream s0 m0 Char arising from a use of let
  • 如何避免编写这种类型的 Haskell 样板代码

    我经常遇到这种情况 这很烦人 假设我有一个 sum 类型 它可以保存一个实例x或一堆其他无关的事情x data Foo x X x Y Int Z String other constructors not involving x 要声明
  • 将名称绑定到值与将值分配给变量

    阅读 Bartosz Milewski 的文章完整的 https www fpcomplete com school starting with haskell basics of haskell 3 pure functions lazi
  • Haskell 类型定义,=> 等

    我正在使用 Learn You a Haskell 来学习 Haskell 第 54 页上有一个 像这样执行 take Num i Ord i gt i gt a gt a take n n lt 0 take take n x xs x
  • 为什么使用 reqwest 下载的 PNG 图像的字节与使用 Python 下载的不同?

    我正在尝试使用 reqwest 库来下载 PNG 文件 但是当我下载它时 我看到了其他编程语言的奇怪行为 例如 Python 例如 let content reqwest get https www google es images sea
  • 是否有适用于 Haskell 或 Scala 等函数式语言的 LL 解析器生成器?

    我注意到明显缺乏用函数式语言创建解析器的 LL 解析器 我一直在寻找但没有成功的理想发现是为 ANTLR 风格的 LL 语法生成 Haskell 解析器 语法的模小数重新格式化 并且令我惊讶的是 每个最后一个解析器生成器都具有函数我发现的语
  • 为什么我不能将 Int 类型与 a 类型匹配

    哈斯克尔新手在这里 我在这里尝试做的事情的一个过于简单的例子 test Int gt a test i i Couldn t match expected type a with actual type Int a is a rigid t
  • 数据类型变体之间的转换

    假设我想创建一种数据类型的两种变体 一种具有特定的构造函数 另一种没有它 否则它们是相同的 我想出了这个 LANGUAGE KindSignatures LANGUAGE DataKinds LANGUAGE GADTs data Foo
  • Repa 数组上的并行 mapM

    在我最近的work https github com bgamari mixture model with Gibbs sampling 我一直在充分利用RVar http hackage haskell org packages arch
  • 下标运算符后缀

    C 标准将使用下标的表达式定义为后缀表达式 AFAIK 这个运算符总是带有两个参数 第一个是指向 T 的指针 另一个是枚举或整数类型 因此它应该符合二元运算符的资格 However MSDN http msdn microsoft com
  • 如何在 GHCJS 程序中定期执行操作?

    应该有人使用setInterval通过Javascript 或者使用一些更惯用的基于线程的解决方案 Using setInterval posed 一些挑战 https stackoverflow com questions 3357661
  • `arr fst` 是如何自然变换的?

    I asked 这个问题 https stackoverflow com q 62733726 11143763不久以前 这是关于以下箭头定律 arr fst first f f arr fst Category k gt k b c gt

随机推荐