Записать аргументы командной строки в файл - PullRequest
0 голосов
/ 04 мая 2018

У меня проблемы с выполнением этого задания. Мы должны принимать аргументы командной строки в качестве входных данных и записывать их в файл. Пока что я сделал ниже код:

val args = CommandLine.arguments()

val a = length args;

val os = TextIO.openOut "rodr4719.txt";

TextIO.output(os, "rodr4719");


fun writeFile(os, args, a) = 


if a = -1 then

TextIO.output(os, "rodr4719")
TextIO.closeOut os
OS.Process.exit(OS.Process.success)

else

val str = nth (args, a);
TextIO.output(os, str ^"\n" );
a = a-1;
writeFile(os, args, a)
end;

writeFile(os, args, a-1)

и полученная ошибка

.sml:22.1 Error: syntax error: inserting EQUALOP

1 Ответ

0 голосов
/ 04 мая 2018

Это в некоторой степени дубликат следующих двух вопросов и ответов:

Вот некоторые общие отзывы:

  • Отступ: Вы можете сделать код немного более читабельным, добавив отступ.

    fun writeFile (os, args, a) = 
        if a = -1 then
            TextIO.output(os, "rodr4719")
            TextIO.closeOut os
            OS.Process.exit(OS.Process.success)
        else
            val str = nth (args, a);
            TextIO.output(os, str ^"\n" );
            a = a-1;
            writeFile(os, args, a)
    end
    

    (Этот код все еще не работает по нескольким причинам, перечисленным ниже.)

  • end: Это ключевое слово не обязательно, когда вы просто объявляете fun. Это происходит в нескольких местах в синтаксисе SML, в частности как let ... in ... end, и вы, вероятно, видели его там.

  • Имена переменных: Я бы назвал входные аргументы по-другому: os звучит как операционная система , когда это, вероятно, аббревиатура для outtream . Просто назовите это outstream. Некоторые люди любят называть это fd для дескриптор файла , но это все о понимании предполагаемой аудитории. args может быть более общим: эта функция может записывать любой список строк, а не только аргументы командной строки. Просто назовите это lines. a звучит так, как будто это может быть любой тип, но это просто целое число. Я бы назвал это n, но на самом деле я бы не использовал число для определения моего состояния остановки.

  • Точки с запятой: Когда ваша функция выполняет несколько действий с побочными эффектами, вы можете использовать оператор ;. Вы не делаете это в части затем , и вы делаете это неправильно в части else , по общему признанию, потому что оператор ; в SML - синтаксически сбивает с толку .

    Краткая версия: точки с запятой в val foo = x; val bar = y; отличаются от точек с запятой в val foo = (TextIO.output (fd, x); TextIO.output (fd, y)).

    Если вам нужен оператор точки с запятой expression , поместите выражение с точкой с запятой в круглые скобки или выражение let. В противном случае он интерпретируется как (необязательный) оператор точки с запятой объявление .

  • При переборе списка строк вместо сохранения счетчика выполняйте сопоставление с образцом в списке. Это значительно упрощает, когда ваша рекурсия заканчивается, так как вы всегда в любом случае обращаетесь к главе списка по очереди.

  • Не выходите: Вы, вероятно, не хотите выходить из программы, когда закончите; выхода из функции должно быть достаточно. Таким образом, вы можете повторно использовать функцию в более сложном потоке управления, не выходя из нее от имени всей программы.

  • Открывайте и закрывайте файлы в одной области: Это общий совет по программированию: когда вы имеете дело с побочными эффектами, убедитесь, что вы не забыли закрыть то, что вы ' Если вы открываете что-то и не закрываете что-то дважды, выполнение этого в одной и той же области кода значительно упрощает рассуждения о правильности. Вы можете сделать простой функцией, которая всегда имеет дело с открытием и закрытием .

  • val внутри тела функции: Если вам нужно временное связывание значения внутри тела функции или какого-либо выражения, используйте выражение let. Кстати, кто-то просто спросил Почему я использую `let`, а не просто` val` для объявления переменной внутри функции в SML? сегодня.

  • Неизменяемые значения : В SML вы не можете обновить значение, подобное a = a-1. a не переменная, а привязка значения . Вы можете повторно связать его с новым значением, основанным на старом, таким образом, затеняя старое, но в этом нет необходимости. Вместо этого выполните рекурсивный вызов с обновленным значением.

Я изменил вашу программу с учетом этих вещей:

fun writeFile (outstream, []) = ()
  | writeFile (outstream, line::lines) =
    ( TextIO.output (outstream, line ^ "\n") ; writeFile (outstream, lines) )

(* Demo *)
val myFile = TextIO.openOut "rodr4719.txt"
val _ = writeFile (myFile, CommandLine.arguments ())
val _ = TextIO.closeOut myFile

(* val _ = ... - это причудливый способ оценки выражений на верхнем уровне без непосредственного выражения на верхнем уровне. В интерактивном REPL foo(); в отдельной строке означает val it = foo();, но как только вы компилируете свои программы для создания автономных двоичных исполняемых файлов, это больше не работает. val _ = ... в основном отбрасывает результат вычисления после его выполнения, и поскольку оба writeFile и TextIO.closeOut возвращают (), они ' не стоит связываться ни с чем.)

...