簡體   English   中英

如何在Haskell中實現二進制數

[英]How to implement Binary numbers in Haskell

我已經看到教會數字的以下數據構造函數

data Nat = Zero | Succ Nat deriving Show

但這是一元數字。 我們如何以這種方式在Haskell中實現二進制數的數據構造函數?

我試過這個:

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

在此之后我們可以得到,十進制5編碼為BinC [One,Zero,One]

但我想我在這里遺漏了一些東西。 我的解決方案似乎不像教會的解決方案那么聰明。 毫不奇怪,我不是教會。 一點點思考,我發現我的解決方案依賴於列表,而Nat不依賴於列表之類的任何外部結構。

我們是否可以使用Succ類型的二進制數構造函數編寫類似於Church的解決方案? 如果有,怎么樣? 我嘗試了很多,但似乎我的大腦無法擺脫列表或其他一些這樣的結構的需要。

我能想到的最接近的就是

λ> 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

只是您收到的其他答案的附錄:

您創建的數據值實際上是Peano數字,而不是Church數字。 它們密切相關,但它們實際上是彼此雙重/反向的。 Peano數字建立在從Set概念構造數字的概念之上,在Haskell中我們使用與數據類型密切相關的概念來表示。

{-# LANGUAGE RankNTypes #-}

import Prelude hiding (succ)

data Peano = Zero
           | Succ Peano
  deriving (Show)

另一方面,教堂數字將數字編碼為函數

type Church = forall n. (n -> n) -> n -> n

zero :: Church
zero = \p -> id

succ :: Church -> Church
succ = \n p -> p . n p

現在,你可以把它們放在一起:

peano :: Church -> Peano
peano c = c Succ Zero

fold :: forall n. (n -> n) -> n -> Peano -> n
fold s z Zero     = z
fold s z (Succ p) = s (fold s z p)

church :: Peano -> Church
church p = \s z -> fold s z p

因此,教堂數字本質上是Peano數字的折疊 而且(peano . church)是Peano數字的標識,盡管上面給出的類型Haskell不會讓你直接把它們組合起來。 如果省略類型聲明,Haskell將推斷出足夠多的類型,您可以將它們組合起來。

在Ralf Hinze的理論珍珠教堂數字中,函數式編程的背景下,有一個很好的概述差異和它們之間的關系,兩次!

你可以進一步概括這種二元性; Peano數字本質上是自然數的初始F-代數,教會數字本質上是自然數的最終/終端F-代數。 對此的一個很好的介紹是Bart Jacobs和Jan Rutten 關於(Co)代數和(Co)歸納A教程

data Bit = Zero | One
data Bin = E Bit | S Bit Bin

five = S One (S Zero (E One))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM