2016-12-03 4 views
0

Я пытаюсь записать аргументы командной строки из моей программы SML в файл, каждый на отдельной строке. Если бы я был бежать sml main.sml a b c easy as 1 2 3 в командной строке, желаемый результат будет иметь файл с содержимым:Ошибка синтаксиса: Вставка EQUALOP

a 
b 
c 
easy 
as 
1 
2 
3 

Однако я получаю следующий вывод из SML:

$ sml main.sml a b c easy as 1 2 3 
val filePath = "/Users/Josue/Desktop/espi9890.txt" : string 
val args = ["a","b","c","easy","as","1","2","3"] : string list 

main.sml:4.21 Error: syntax error: inserting EQUALOP /usr/local/smlnj/bin/sml: Fatal error -- Uncaught exception Compile with "syntax error" raised at ../compiler/Parse/main/smlfile.sml:15.24-15.46

С помощью этого кода:

val filePath = "/Users/Josue/Desktop/espi9890.txt"; 
val args = CommandLine.arguments(); 

fun writeListToFile x = 
    val str = hd x^"\n"; 
    val fd = TextIO.openAppend filePath; 
    TextIO.output (fd, str); 
    TextIO.closeOut fd; 
    writeListToFile (tl x); 
| fun writeListToFile [] = 
    null; 

writeListToFile args; 

ли я что-то отсутствует?

ответ

3

Правильный синтаксис для объявления вложенных значений является:

fun writeListToFile (s::ss) = 
    let val fd = TextIO.openAppend filePath 
     val _ = TextIO.output (fd, s^"\n") 
     val _ = TextIO.closeOut fd 
    in writeListToFile ss end 
    | writeListToFile [] =() 

То есть,

  1. (ошибка) Вы забыли о let ... in ... end.

  2. (ошибка) Ваш второй шаблон, [], никогда не будет соответствовать, потому что первый, x, является более общим и соответствует всем входным списки (в том числе пустую). Поэтому, даже если ваша синтаксическая ошибка была исправлена, эта функция будет зацикливаться до тех пор, пока она не сработает, потому что вы пытаетесь принять hd/tl пустого списка.

  3. (ошибка) Если функция имеет несколько случаев матча, только первый из них должен быть префиксом fun, а остальные должны быть | вместо этого. (Вы можете свободно решать, как отступ это.)

  4. (Error) Есть два вида запятой в SML: Один для разделения деклараций, и один оператор, который отбрасывает значение (но не эффект) его первого операнда. Первый вид, который разделяет объявления, всегда можно избежать. Второй вид - это тот, который вы пытаетесь использовать для объединения нескольких выражений, каждый из которых имеет желаемый (файловый ввод-вывод) эффект (и эквивалентен наличию let-выражений с несколькими эффективными объявлениями в строке, как указано выше) ,

    Но ... на верхнем уровне (например, в теле функции) SML не может определить разницу между двумя типами точек с запятой, так как они оба могут встречаться там. В конце концов, первый вид, который мы хотим избежать, обозначает окончание тела функции, а второй вид просто отмечает конец подвыражения в теле функции.

    Способ избежать этой двусмысленности заключается в том, чтобы обернуть оператор ;, где объявления не допускаются, например. между in и end, или внутри скобки.

  5. (Ошибка) Нет смысла возвращать эту функцию null. Вероятно, вы думали nil (пустой список, ака []), но val null : 'a list -> bool - это функция!Действительно, бессмысленно иметь возвращаемое значение для этой функции. Во всяком случае, это может быть bool, указывающий, были ли строки написаны успешно (в этом случае вам, вероятно, придется обрабатывать исключения IO). Самое близкое к функции, которая ничего не возвращает, - это функция, которая возвращает тип единица (со значением ()).

  6. (Предложение) Вы можете использовать hd/tl разделить список, но вы также можете использовать поиск по шаблону. Используйте сопоставление шаблонов, как и примеры, которые я дал.

  7. (Предложение) Вы можете использовать полуколоны вместо объявлений val _ = ...; также; это просто вопрос вкуса. Например .:

    fun writeListToFile (s::ss) = 
        let val fd = TextIO.openAppend filePath 
        in TextIO.output (fd, s^"\n") 
        ; TextIO.closeOut fd 
        ; writeListToFile ss 
        end 
        | writeListToFile [] =() 
    
  8. (Предложение) Это довольно глупо, что каждый раз, когда функция вызывает саму себя, она открывает файл, присоединяет и закрывает файл. В идеале вы только открыть и закрыть файл один раз:

    fun writeListToFile lines = 
        let val fd = TextIO.openAppend filePath 
         fun go [] = TextIO.closeOut fd 
          | go (s::ss) = (TextIO.output (fd, s^"\n") ; go ss) 
        in go lines end 
    
  9. (Предложение) Поскольку вы делаете то же самое для каждого элемента в списке, вы можете также рассмотреть возможность использования функции высшего порядка, обобщающую итерация. Как правило, это было бы val map : ('a -> 'b) -> 'a list -> 'b list, но так как TextIO.output она возвращает блок, очень похоже val app : ('a -> unit) -> 'a list -> unit еще лучше:

    fun writeListToFile lines = 
        let val fd = TextIO.openAppend filePath 
        in List.app (fn s => TextIO.output (fd, s^"\n")) lines 
        ; TextIO.closeOut fd 
        end 
    
  10. (Предложение) Наконец, вы можете вызывать эту функцию appendListToFile, или просто appendLines и возьмите filePath в качестве аргумента функции, так как filePath подразумевает, что это файл, и функция добавляет линейные разрывы к каждому s. Имена важны.

    fun appendLines filePath lines = 
        let val fd = TextIO.openAppend filePath 
        in List.app (fn s => TextIO.output (fd, s^"\n")) lines 
        ; TextIO.closeOut fd 
        end 
    
+1

У вас есть много терпения. Удивительно, что SML/NJ, похоже, попадает во внутреннюю ошибку при компиляции этого кода, а не просто отказывается от него с помощью диагностического сообщения. –

+0

Хе-хе, спасибо. Трудно дать подробное сообщение об ошибке при сбое анализа. Кажется, что Java/C# IDE лучше справляются с этим во многих случаях; кажется, что они могут возобновить анализ за точкой отказа и предоставить частичное синтаксическое дерево. Я не знаю, как это делается на этом. –

+0

Хмм, пытаясь передать в 'filePath' и' args', дает мне 'Ошибка: оператор не является функцией [tycon mismatch] operator: unit', любая идея, почему это может быть? Сообщения об ошибках SML очень сбивают меня с толку –