【问题标题】:How to read from stdin?如何从标准输入读取?
【发布时间】:2016-03-25 05:10:37
【问题描述】:

如何在 Clean 中执行此操作?

伪代码:

loop:
    input = read_stdin
    if input == "q":
        break loop
    else:
        print "you input: ", input

其实,我看过一些pdf。但是我有一个想象力,很难处理标准输入和标准输出。我可以有一个使用 stdio 的代码示例吗?

按照基兰的指示,我完成了我的小程序。

module std_in_out_loop
import StdEnv

loop :: *File -> *File
loop io
# io = fwrites "input your name: " io
# ( name, io ) = freadline io
# name = name % ( 0, size name - 2 )
| name == "q"
# io = fwrites "Bye!\n" io
= io
| name == ""
# io = fwrites "What's your name?\n" io
= loop io
| otherwise
# io = fwrites ( "hello " +++ name +++ "\n" ) io
= loop io

Start:: *World -> *World
Start world
# ( io, world ) = stdio world
# io = loop io
# ( ok, world ) = fclose io world
| not ok = abort "Cannot close io.\n"
| otherwise = world

【问题讨论】:

  • 为什么不用google找教程自己做呢?
  • 我找不到那个。你能给我一个链接吗?请。谢谢。
  • 谢谢!你介意给我一些代码吗?我知道这对你来说是小菜一碟。

标签: functional-programming stdio clean-language uniqueness-typing


【解决方案1】:

来自Clean 2.2 manual,第 9 章:

虽然 Clean 是纯函数式的,但允许有副作用的操作(例如 I/O 操作)。达到 这在不违反语义的情况下,为经典类型提供了所谓的唯一性属性。如果一个论点 一个函数的对象被指示为唯一的,保证在运行时对应的实际对象是本地的,即有 没有其他参考。显然,可以安全地对这种“唯一对象”进行破坏性更新。

具体来说,您可以将 Start(通常具有 arity 0(不带参数))设为从 *World*World 的函数。这个想法是我们现在有了一个改变世界的函数,这意味着允许副作用(它们不再是真正的副作用,而是对世界的操作)。

* 表示World 类型的唯一性。这意味着你永远不能有两个 world 参数的实例。例如,以下将给出编译时唯一性错误:

Start :: *World -> *(*World, *World)
Start w = (w, w)

要使用标准 IO,您将需要来自 StdEnv 中的 StdFile 模块的函数。您将需要的功能是:

stdio :: !*World -> *(!*File, !*World)
fclose :: !*File !*World -> !(!Bool, !*World)

我稍微简化了类型,实际上它们来自 FileSystem 类。 stdio 从一个世界中打开一个唯一 File 并返回新的、修改后的世界。 fclose 关闭世界中的文件,并返回成功标志和修改后的世界。

然后,要从该 stdio 文件读取和写入,您可以使用:

freadline :: !*File -> *(!*String, !*File)
fwrites :: !String !*File -> !*File

freadline 将一行读入一个字符串,包括换行符。 fwrites 将字符串写入文件,通常您希望在写入 stdio 时包含换行符。

把它放在一起:

Start :: *World -> *World
Start w
# (io,w) = stdio w                                // open stdio
# io = fwrites "What is your name?\n" io          // ask for name
# (name,io) = freadline io                        // read in name
# name = name % (0, size name - 2)                // remove \n from name
# io = fwrites ("Hello, " +++ name +++ "!\n") io  // greet user
# (ok,w) = fclose io w                            // close stdio
| not ok = abort "Couldn't close stdio"           // abort in case of failure
= w                                               // return world from Start

# 语法对您来说可能是新的。是let的一种,允许你对文件(或其他东西)使用相同的名称,比使用更方便,例如:

Start w = w3
where
    (io, w1) = stdio w
    io1 = fwrites "What is your name?\n" io
    (name, io2) = freadline io1
    //...
    (ok, w3) = fclose io10 w2

现在您应该能够使用辅助函数 loop :: *File -> *File 在伪代码中执行您想要的操作,该函数会递归调用自身,直到输入 q

除了freadlinefwrites 之外,还有更多功能,请参阅StdFile.dcl 了解想法。

【讨论】:

  • 嗨,卡米尔,很高兴再次见到你。你会介意的?给我一个icl文件?这相当于伪代码。让我玩吗?然后,我会回来阅读你的指示。这可能是愚蠢的?我不知道?请。谢谢!
  • @sama '把它放在一起'之后的东西是一个现成的例子,只需添加一个模块头和import StdEnv。尝试自己写loop :: *File -> *File,正如我在上一段中建议的那样。如果您不管理,请提出一个新问题,包括您的代码和一个具体问题。谢谢!
  • 嗨,卡米尔!再次感谢。为了我? CLEAN 很不一样,Start 是唯一一次输出东西的机会吗? (其他语言可以在任何地方做到这一点。例如,对于python?如果Yoy想阅读一些东西?或输出一些东西?这很容易。我可以谷歌搜索,并为自己编写一些代码)但是,这对我来说很难。抱歉,我不知道将它们分组到一个 icl 文件中。
  • @sama 是的,函数式语言中的 IO 有点不同。但是,Start 并不是唯一输出某些东西的机会。同样,上面“将其放在一起”下的一个代码块是一个完整的示例。如果这太难并且您需要更多解释,请参阅Functional Programming in Clean, chapter 5
  • 嗨!卡米尔!这不是真的?我玩这个,把它放在一起它只读取一次标准输入,输出一次。不是循环吗? (我复制它,然后按 F5)