内容简介:首先我自己想了一个比较 naive 的实现,是把于是我写了一个很丑很幼稚很年轻很简单的实现,最后发现传递证明比较繁琐,肯定是不理想的,遂放弃(并没有很好地理由 dependent type 的类型系统对输入做约束)。代码已经写不下去了,存了一个在gist上。
首先我自己想了一个比较 naive 的实现,是把 printf
的填充变量的参数做成一个 List
,然后对这个 List
通过 dependent function 的特性进行一些限制,达到类型安全的效果(比如传入一个该 List
合法的证明)。
于是我写了一个很丑很幼稚很年轻很简单的实现,最后发现传递证明比较繁琐,肯定是不理想的,遂放弃(并没有很好地理由 dependent type 的类型系统对输入做约束)。代码已经写不下去了,存了一个在gist上。
然后我看到了 dram 的回答,写的很 inspiring(首先用一个函数根据输入类型返回 printf
的类型,然后再写真正的实现),然后我学习了一波之后弄了一个 Agda 的。由于 Agda 对浮点的支持好像很挫,我就把对浮点数的支持改成了对 Char
类型的支持。
我们先导入一些必需的东西,需要标准库。
module Printf where open import Data.List using (List; _∷_; []) open import Data.Char renaming (Char to ℂ; show to showℂ) open import Data.Nat using (ℕ; _+_) open import Data.Nat.Show renaming (show to showℕ) open import Data.Empty open import Relation.Binary.PropositionalEquality using (_≡_; refl) open import Relation.Nullary using (yes; no) open import Function open import Coinduction open import Data.String using (toList; fromList; String; _++_)
思路是:
-
对格式化字符串进行解析,返回一个带元信息的
List -
实现一个带元信息的
List到完整printf类型的函数 -
实现
printf
首先我们需要定义这个带元信息的 List
的成员的 ADT:
data Fmt : Set where fmt : ℂ → Fmt lit : ℂ → Fmt
然后实现『根据格式化字符判断类型』的函数和『根据格式化字符和对应的类型的值返回字符串』的函数(过于简单,不予赘述。全部照抄 dram 版本):
ftype : ℂ → Set ftype 'd' = ℕ ftype 'c' = ℂ ftype _ = ⊥ format : (c : ℂ) → ftype c → String format 'd' = showℕ format 'c' c = fromList $ c ∷ [] format _ = const ""
然后实现『解析格式化字符串,返回带元信息的List』:
parseF : List ℂ → List Fmt parseF [] = [] parseF (x ∷ xs) with xs | x ≟ '%' ... | '%' ∷ xss | yes _ = lit '%' ∷ parseF xss ... | c ∷ xss | yes _ = fmt c ∷ parseF xss ... | [] | yes _ = lit '%' ∷ [] ... | _ | no _ = lit x ∷ parseF xs
然后这个函数 Agda 给我报了个 termination error,我觉得不大对劲。于是我去 问了
,维护者说是因为 with
给函数加了一层 pm 导致 Agda 无法判断这个函数是正确的。
这也太智障了吧,但好在我看懂了 dram 的实现,就重写了一个不用 with
的(其实我也不是很懂为什么 dram 要用 with
,我觉得超难用):
parseF : List ℂ → List Fmt
parseF [] = []
parseF ('%' ∷ '%' ∷ cs) = lit '%' ∷ parseF cs
parseF ('%' ∷ c ∷ cs) = fmt c ∷ parseF cs
parseF ( c ∷ cs) = lit c ∷ parseF cs
然后这个就 check 了,我们再实现『根据格式化字符串判断 printf
完整类型』的函数:
ptype : List Fmt → Set ptype [] = String ptype (fmt x ∷ xs) = ftype x → ptype xs ptype (lit x ∷ xs) = ptype xs printfType : String → Set printfType = ptype ∘ parseF ∘ toList
最后实现 printf
的逻辑:
printfImpl : (fmt : List Fmt) → String → ptype fmt printfImpl [] pref = pref printfImpl (fmt x ∷ xs) pref val = printfImpl xs $ pref ++ format x val printfImpl (lit x ∷ xs) pref = printfImpl xs $ pref ++ (fromList $ x ∷ [])
然后把它包一层:
printf : (fmt : String) → printfType fmt printf s = printfImpl (parseF $ toList s) ""
随手证明:
proof₀ : printf "Hello, World!"
≡ "Hello, World!"
proof₀ = refl
proof₁ : printf "%% %% (%d + %d = %d) (toChar %d = %c)" 114 514 628 6 '6'
≡ "% % (114 + 514 = 628) (toChar 6 = 6)"
proof₁ = refl
我们还可以加入字符串支持。 修改以下函数:
ftype : ℂ → Set ftype 'd' = ℕ ftype 'c' = ℂ ftype 's' = String ftype _ = ⊥ format : (c : ℂ) → ftype c → String format 'd' = showℕ format 'c' c = fromList $ c ∷ [] format 's' = id format _ = const ""
然后验证:
proof₂ : printf "%d岁,是%s" 24 "学生"
≡ "24岁,是学生"
proof₂ = refl
错误情况的证明:
proof₃ : printf "%d岁,是%s" 25 "学生"
≢ "24岁,是学生"
proof₃ ()
怎么样,是不是妙不可言。
错误情况的证明需要修改一句 import
:
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl)
以上所述就是小编给大家介绍的《Agda 实现类型安全的 printf》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 在 TS 中如何实现类型保护?类型谓词了解一下
- 在 TS 中如何实现类型保护?类型谓词了解一下
- Spring 早期类型转换,基于 PropertyEditor 实现
- 基于 Kubernetes 的 GPU 类型调度实现
- iOS – tableView类型的筛选框实现
- Golang反射机制的实现分析——查询类型名称
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
An Introduction to the Analysis of Algorithms
Robert Sedgewick、Philippe Flajolet / Addison-Wesley Professional / 1995-12-10 / CAD 67.99
This book is a thorough overview of the primary techniques and models used in the mathematical analysis of algorithms. The first half of the book draws upon classical mathematical material from discre......一起来看看 《An Introduction to the Analysis of Algorithms》 这本书的介绍吧!