2016-12-14 6 views
4

У меня есть следующие функции, которые преобразуют CSV файлы в конкретные текстовых схемы (ожидаемых CNTKTextFormat чтение):F # Запись в файл изменение поведения на типе возвращаемого значения

open System.IO 
open FSharp.Data; 
open Deedle; 

let convert (inFileName : string) = 
    let data = Frame.ReadCsv(inFileName) 
    let outFileName = inFileName.Substring(0, (inFileName.Length - 4)) + ".txt" 
    use outFile = new StreamWriter(outFileName, false) 
    data.Rows.Observations 
    |> Seq.map(fun kvp -> 
     let row = kvp.Value |> Series.observations |> Seq.map(fun (k,v) -> v) |> Seq.toList 
     match row with 
     | label::data -> 
      let body = data |> List.map string |> String.concat " " 
      outFile.WriteLine(sprintf "|labels %A |features %s" label body) 
      printf "%A" label 
     | _ -> 
      failwith "Bad data." 
    ) 
    |> ignore 

Как ни странно, выходной файл пуст после запуска интерактивная панель F #, и что printf не дает печати вообще.

Если удалить ignore, чтобы убедиться, что есть фактические строки, обрабатываемые (подтвержденные возвращающая последующие статьи нулей), вместо пустого файла я получаю:

val it : seq<unit> = Error: Cannot write to a closed TextWriter.

Раньше я объявив StreamWriter с использованием let и удалив его вручную, но я также создал пустые файлы или несколько строк (скажем, 5 из тысяч).

Что здесь происходит? Также, как исправить запись файла?

+0

'Seq.map' ленив. То, что вы хотите, это 'Seq.iter', который принимает функцию побочного эффекта (тот, который возвращает' unit'), и, НЕ ЛЕЖНО, применяет его к каждому элементу в seq. – rmunn

ответ

7

Seq.map возвращает ленивую последовательность, которая не оценивается до тех пор, пока она не будет повторена. Вы в настоящее время не перебираете его в пределах convert, поэтому никакие строки не обрабатываются. Если вы вернете Seq<unit> и перейдете через него convert, outFile уже будет закрыт, поэтому вы видите исключение.

Вы должны использовать вместо Seq.iter:

data.Rows.Observations 
    |> Seq.iter (fun kvp -> ...) 
2

Как Ли уже упоминалось, Seq.map ленив. И именно поэтому вы получаете «Нельзя писать закрытому TextWriter»: ключевое слово use располагает своим IDisposable, когда оно выходит за рамки. В этом случае это в конце вашей функции. Так как Seq.map ленив, ваша функция возвращала объект unevaluated, который был закрыт над StreamWriter в вашем заявлении use, но к тому моменту, когда вы оценили эту последовательность (в любой части кода, проверенной для нулевых значений Seq, или в интерактивном окне F #), StreamWriter уже был удален путем выхода из области видимости.

Сменить Seq.map на и устранить обе эти проблемы.

3

Помимо упомянутых выше решений, вы также можете избежать StreamWriter и использовать одну из стандартных функций .Net, File.WriteAllLines. Вы бы подготовить последовательность преобразованных строк, а затем написать, что в файл:

let convert (inFileName : string) = 
    let lines = 
     Frame.ReadCsv(inFileName).Rows.Observations 
     |> Seq.map(fun kvp -> 
      let row = kvp.Value |> Series.observations |> Seq.map snd |> Seq.toList 
      match row with 
      | label::data -> 
       let body = data |> List.map string |> String.concat " " 
       printf "%A" label 
       sprintf "|labels %A |features %s" label body 
      | _ -> 
       failwith "Bad data." 
     ) 
    let outFileName = inFileName.Substring(0, (inFileName.Length - 4)) + ".txt" 
    File.WriteAllLines(outFileName, lines) 

Update на основе обсуждения в комментариях: Вот решение, которое позволяет избежать Deedle вообще. Я делаю некоторые предположения о вашем формате входного файла здесь, основываясь на другом вопросе, который вы опубликовали сегодня: Ярлык находится в столбце 1, функции следуют.

let lines = 
    File.ReadLines inFileName 
    |> Seq.map (fun line -> 
     match Seq.toList(line.Split ',') with 
     | label::data -> 
      let body = data |> List.map string |> String.concat " " 
      printf "%A" label 
      sprintf "|labels %A |features %s" label body 
     | _ -> 
      failwith "Bad data." 
    ) 
+1

Прохладная вещь: вам не нужно беспокоиться о размере файла, последовательности ленивы (ну, по крайней мере, если ReadCsv делает все правильно, что, я полагаю, это так) –

+0

О, мой, не я узнал что-нибудь. Есть ли способ делать такую ​​ленивую загрузку в строках входных файлов? – VillasV

+2

'File.ReadLines' - ваш друг –

 Смежные вопросы

  • Нет связанных вопросов^_^