String 如何在agda中将数字转换为字符串?

String 如何在agda中将数字转换为字符串?,string,agda,String,Agda,我需要写一些东西,用agda将数字转换成字符串。我发现以前有人问过如何将字符串传输到agda 我想把它倒过来用 row-to-stringh : (m : ℕ) → string row-to-stringh 0 = "0" row-to-stringh 1 = "1" row-to-stringh 2 = "2" row-to-stringh 3 = "3" row-to-stringh 4 = "4" row-to-stringh 5 = "5" row-to-stringh 6 = "6

我需要写一些东西,用agda将数字转换成字符串。我发现以前有人问过如何将字符串传输到agda

我想把它倒过来用

row-to-stringh : (m : ℕ) → string
row-to-stringh 0 = "0"
row-to-stringh 1 = "1"
row-to-stringh 2 = "2"
row-to-stringh 3 = "3"
row-to-stringh 4 = "4"
row-to-stringh 5 = "5"
row-to-stringh 6 = "6"
row-to-stringh 7 = "7"
row-to-stringh 8 = "8"
row-to-stringh 9 = "9"
row-to-stringh _ = ""

但这还不够好。当数字大于9时,它只会将其转换为“”,而不是“(该数字)”。有人能帮我吗?

如果你不想自己实现这个函数,标准库中有一个函数

如果你想自己写:把一个数字转换成一个字符串的通常方法是用余数反复除法来提取数字。例如(余数用括号表示):

然后,您只需将剩余的字符收集到列表中,将其反转,并将数字转换为字符。在Agda中尝试这一点可能也很有诱惑力,但是,您将在终止检查器中遇到问题

首先,您必须说服它,
divMod
(即余数除法-模)终止。您只需将除数硬编码到函数中,就可以轻松说服终止检查器

最难的部分是,重复地将数字除以10实际上是终止的。这很可能涉及一些相当复杂的技巧(如有充分根据的递归)


如果您想知道它是如何以这种方式完成的,请查看上面链接的实现。不管怎样,有一种效率稍低但更简单的方法

让我们用自然数列表来表示数字

Digits = List ℕ
我们想编写一个函数
addOne
,它(顾名思义)将一个数字添加到由数字列表表示的数字中,即:

addOne : Digits → Digits
为此,我们将使用基本的纸笔方法:在最低有效位上加一;如果结果小于10,我们就完成了;如果不是,则写入0并将1带到下一位。这是我们的随身行李:

data Carry : Set where
  +0 : Carry
  +1 : Carry
这里是执行加法的函数-第二个
进位
参数可以看作是前两个数字相加的进位

ripple-carry : Digits → Carry → Digits
ripple-carry ns       +0 = ?
ripple-carry []       +1 = ?
ripple-carry (n ∷ ns) +1 with suc n ≤? 9
... | yes _ = ?
... | no  _ = ?
实际实现是一个练习-使用上面给出的描述。请注意,我们以相反的顺序存储数字(这允许更高效和更容易的实现)。例如,123由
3表示∷ 2.∷ 1.∷ []
和0由
[]
执行

我们可以恢复
addOne
功能:

addOne : Digits → Digits
addOne n = ripple-carry n +1
其余的只是管道

toDigits : ℕ → Digits
toDigits zero    = []
toDigits (suc n) = addOne (toDigits n)

show : ℕ → String
show 0 = "0"
show n = (fromList ∘ map convert ∘ reverse ∘ toDigits) n
  where
  convert : ℕ → Char
  convert 0 = '0'
  convert 1 = '1'
  convert 2 = '2'
  convert 3 = '3'
  convert 4 = '4'
  convert 5 = '5'
  convert 6 = '6'
  convert 7 = '7'
  convert 8 = '8'
  convert 9 = '9'
  convert _ = ' ' -- Never happens.

使用的模块:

open import Data.Char
open import Data.List
open import Data.Nat
open import Data.String
open import Function
open import Relation.Nullary

我做了一些测试,结果表明这种方法实际上相当有效(特别是与标准库中的函数相比)

对于给定的数字n,上述算法需要访问O(n)个数字(
addOne
在90%的情况下只需要访问一个数字,在9%的情况下需要访问两个数字,在0.9%的情况下需要访问三个数字,等等)。除非我们有一些更快的基本操作(例如在幕后使用Haskell的
Integer
),否则这大概是我们能得到的最快的操作——毕竟我们使用的是一元数

标准库使用上面提到的重复除法,这也是(除非我的数学是错的)O(n)。然而,这还不包括对证据的处理,这会增加巨大的开销,使其停止。让我们做一个比较:

open import Data.Nat
open import Data.Nat.Show
open import Function
open import IO

main = (run ∘ putStrLn ∘ show) n
下面是编译代码的时间(在Emacs中使用
C-ccc-xc-C
)<代码>显示标准库中的:

n       time
———————————————
1000     410 ms
2000    2690 ms
3000    8640 ms
如果我们使用上面定义的
show
,我们会得到:

n         time
———————————————
100000    26 ms
200000    41 ms
300000    65 ms

展示文档现在位于此处:
n         time
———————————————
100000    26 ms
200000    41 ms
300000    65 ms