Как читать со стандартного ввода?
Как это сделать в 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
1 ответ
Из руководства Clean 2.2, глава 9:
Хотя Clean является чисто функциональным, операции с побочными эффектами (например, операции ввода-вывода) разрешены. Чтобы достичь этого без нарушения семантики, классические типы снабжаются так называемыми атрибутами уникальности. Если аргумент функции указан как уникальный, то гарантируется, что во время выполнения соответствующий фактический объект является локальным, то есть нет других ссылок на него. Очевидно, что разрушительное обновление такого "уникального объекта" может быть безопасно выполнено.
Конкретно можно сделать Start
, которая обычно имеет арность 0 (не принимает аргументов), функция из *World
в *World
, Идея состоит в том, что теперь у нас есть функция, которая меняет мир, что означает, что побочные эффекты разрешены (на самом деле они больше не побочные эффекты, а операции в мире).
*
указывает на уникальность World
тип. Это означает, что у вас не может быть двух примеров мирового спора. Например, следующее даст ошибку уникальности во время компиляции:
Start :: *World -> *(*World, *World)
Start w = (w, w)
Для использования стандартного ввода-вывода вам понадобятся функции из StdFile
модуль в StdEnv
, Функции, которые вам понадобятся:
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
вводится.
Есть больше функций, чем только freadline
а также fwrites
, увидеть StdFile.dcl
для идеи.