我正在尝试编写一个简单的程序,从标准输入读取一行,将其反转,然后打印反转的字符串。
不幸的是,原生getLine
函数会读取Costring
;我只能反转String
s;并且没有将Costring
带到String
的功能。
如何修改此程序进行编译?
module EchoInputReverse where
-- Agda standard library 0.7
open import Data.List using (reverse)
open import Data.String
open import Foreign.Haskell using (Unit)
open import IO.Primitive
postulate
getLine : IO Costring
{-# COMPILED getLine getLine #-}
main : IO Unit
main =
getLine >>= (λ s →
-- NOTE: Need a (toString : Costring → String) here. Or some other strategy.
return (toCostring (fromList (reverse (toList (toString s))))) >>= (λ s' →
putStrLn s'))
答案 0 :(得分:10)
你不能这样做,至少不是直接的。问题是Costring
的大小可能是无限的,而String
s必须是有限的。
想象一下将程序作为prog < /dev/zero
运行,会发生什么? reverse
函数只有在到达输入列表的末尾后才能生成第一个元素,并且可能永远不会发生。
我们需要表达的是,将Costring
转换为String
可能会失败。一种方法是使用偏好monad。我们来看看定义:
data _⊥ {a} (A : Set a) : Set a where
now : (x : A) → A ⊥
later : (x : ∞ (A ⊥)) → A ⊥
因此,我们可以使用A
类型now
类型的值,或者我们需要等待later
。但请注意∞
符号:这意味着我们实际上可以永远等待(因为可以有无数个later
构造函数)。
我将转换和反转合并为一个函数。首先进口:
open import Category.Monad.Partiality
open import Coinduction
open import Data.Char
open import Data.Colist
using ([]; _∷_)
open import Data.List
using ([]; _∷_; List)
open import Data.String
open import Data.Unit
open import IO
现在,reverse
函数的类型应该提到我们将Costring
作为输入,但返回String
可能会失败。然后实现相当简单,它通常与累加器反向:
reverse : Costring → String ⊥
reverse = go []
where
go : List Char → Costring → String ⊥
go acc [] = now (fromList acc)
go acc (x ∷ xs) = later (♯ go (x ∷ acc) (♭ xs))
但是,我们可以打印String
,但不打印String ⊥
!这就是IO
有用的地方:我们可以将later
构造函数解释为“什么都不做”,如果我们找到now
构造函数,我们可以putStrLn
它包含String
putStrLn⊥ : String ⊥ → IO ⊤
putStrLn⊥ (now s) = putStrLn s
putStrLn⊥ (later s) = ♯ return tt >> ♯ putStrLn⊥ (♭ s)
请注意,我使用IO
模块中的IO
,而不是IO.Primitive
中的getLine
模块。这基本上是建立在假设上的一层,所以它更好一些。但是如果你想使用import IO.Primitive as Prim
postulate
primGetLine : Prim.IO Costring
{-# COMPILED primGetLine getLine #-}
getLine : IO Costring
getLine = lift primGetLine
,你必须写:
main
最后,我们可以编写main = run (♯ getLine >>= λ c → ♯ putStrLn⊥ (reverse c))
函数:
C-c C-x C-c
使用$ cat test
hello world
$ prog < test
dlrow olleh
编译此程序然后运行它,我们得到了预期的结果:
{{1}}
答案 1 :(得分:5)
getLine : IO String
代替getLine : IO Costring
。这有效。所以你得到:
module EchoInputReverse where
-- Agda standard library 0.7
open import Data.List using (reverse)
open import Data.String
open import Foreign.Haskell using (Unit)
open import IO.Primitive
postulate
getLine : IO String
{-# COMPILED getLine getLine #-}
main : IO Unit
main =
getLine >>= (λ s →
return (toCostring (fromList (reverse (toList s)))) >>= (λ s' →
putStrLn s'))
缺点是这种方法断言getLine
总是返回一个有限字符串,这在prog < /dev/zero
的情况下可能不正确,因为@Vitus指出。< / p>
但我不认为这很重要。如果getLine
实际上确实返回一个无限字符串,则此解决方案和@ Vitus的解决方案都不会产生终止的程序。他们有相同的行为。
在这种情况下检测输入是否无限并产生错误将是理想的。但是这种无穷大的检测通常是不可能的。