Как читать со стандартного ввода?

Как это сделать в 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 для идеи.

Другие вопросы по тегам