2013-02-19 3 views
2

Я новичок в f #, и я попытался написать программу, предназначенную для прохождения через все файлы в данном каталоге и для каждого файла типа «.txt», чтобы добавить в файл идентификационный номер + «DONE».Как вызвать функцию в Seq.whatever без "printf"?

моя программа:

//const: 
[<Literal>] 
let notImportantString= "blahBlah" 
let mutable COUNT = 1.0 

//funcs: 
//addNumber --> add the sequence number COUNT to each file. 
let addNumber (file : string) = 
let mutable str = File.ReadAllText(file) 
printfn "%s" str//just for check 
let num = COUNT.ToString() 
let str4 = str + " " + num + "\n\n\n DONE" 
COUNT <- COUNT + 1.0 
let str2 = File.WriteAllText(file,str4) 
file 

//matchFunc --> check if is ".txt" 
let matchFunc (file : string) = 
file.Contains(".txt") 

//allFiles --> go through all files of a given dir 
let allFiles dir = 

seq 
    { for file in Directory.GetFiles(dir) do 
     yield file 
      } 

//////////////////////////// 

let dir = "D:\FSharpTesting" 
let a = allFiles dir 
     |> Seq.filter(matchFunc) 
     |> Seq.map(addNumber) 
printfn "%A" a 

Мой вопрос:

Tf Я не пишу последнюю строку (printfn "% A" а) файлы не изменится (если я DO написать эту строку. он работает и меняет файлы) Когда я использую отладчик, я вижу, что он действительно не вычисляет значение «a», когда он прибывает в строку, если «let a = ......» он продолжает printfn а затем, когда он «видит» «а», он возвращается и вычисляет ответ «а». Почему это и как я могу «запустить» функцию без печати?

также - Может ли кто-нибудь сказать мне, почему я должен добавить файл в качестве возвращаемого типа функции «addNumber»? (я добавил это, потому что, как это работает, но я не понимаю, почему ....)

последний вопрос- , если я пишу переменную сразу COUNT после строки определения [] это дает error и говорит, что константа не может быть «изменчивой», но если добавление (и именно поэтому я сделал это) еще одну строку раньше (например, строку), она «забывает» ошибки и работает. почему? и если у вас действительно не может быть mutable const, как я могу сделать статическую переменную?

ответ

9

Если я не буду писать последнюю строку (printfn "% A" a), файлы не будут изменены.

F # последовательности являются ленивыми. Поэтому для принудительной оценки вы можете выполнить некоторую операцию, не возвращая последовательность. Например, вы можете вызвать Seq.iter (имеют побочные эффекты, вернуть ()), Seq.length (возвращает int, который является длиной последовательности) или Seq.toList (возвращает список, нетерпеливые структуры данных) и т.д.

Может кто-нибудь подскажет мне, почему я должен добавить file : string в качестве возвращаемого типа функции addNumber?

Метод и доступ к свойствам не играют хорошенько с выводами типа F #. Контроллер типа работает слева направо, сверху вниз. Когда вы говорите file.Contains, он не знает, какой тип должен быть с Contains. Поэтому аннотация вашего типа является хорошим советом для проверки типа F #.

, если я пишу переменные сразу COUNT после линии [<Literal>] определения он дает ошибку и говорит, что константа не может быть «изменчивой»

Цитируя MSDN:

Значения, которые предназначены для констант, могут быть отмечены атрибутом Literal. Этот атрибут приводит к тому, что значение компилируется как константа.

Изменчивое значение может изменить его значение в какой-то момент вашей программы; компилятор жалуется по уважительной причине. Вы можете просто удалить атрибут [<Literal>].

+0

ОК, я понял! спасибо всем! – nati

1

f # оценивается сверху донизу, но вы создаете только ленивые значения, пока не сделаете printfn. Таким образом, printfn на самом деле является первым, что выполняется, что, в свою очередь, выполняет остальную часть вашего кода. Я думаю, что вы можете сделать то же самое, если будете придерживаться println после Seq.map (addNumber) и сделать toList на нем, что также приведет к оценке.

+0

СПАСИБО ТАК МНОГО! Я РЕАЛЬНО ПРИНЯЛ БЫСТРЫЙ ОТВЕТ! – nati

3

Seq.map предназначен для сопоставления одного значения другому, не в целом для изменения значения. seq<_> представляет собой лениво сгенерированную последовательность, поэтому, как указал Алекс, ничего не произойдет до тех пор, пока последовательность не будет перечислена. Это, вероятно, лучше подходит для codereview, но вот как бы я это пишу:

Directory.EnumerateFiles(dir, "*.txt") 
    |> Seq.iteri (fun i path -> 
    let text = File.ReadAllText(path) 
    printfn "%s" text 
    let text = sprintf "%s %d\n\n\n DONE" text (i + 1) 
    File.WriteAllText(path, text)) 

Seq.map требует тип возвращаемого значения, как и все выражения в F #. Если функция выполняет действие, а не вычисляет значение, оно может возвращать unit: (). Что касается COUNT, значение не может быть mutable и [<Literal>] (const в C#). Это точные противоположности. Для статической переменной, используйте модуль в области видимости let mutable связывания:

module Counter = 
    let mutable count = 1 

open Counter 
count <- count + 1 

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

let count = 
    let i = ref 0 
    fun() -> 
    incr i 
    !i 

let one = count() 
let two = count() 
+0

СПАСИБО ЗА ВАШ ОТВЕТ! – nati

3

Чтобы уточнить ответ Алекса - последовательности F # оцениваются лениво. Это означает, что каждый элемент в последовательности генерируется «по требованию».

Преимущество этого в том, что вы не тратите время на вычисления и память на элементы, которые вам никогда не нужны. Ленивая оценка немного привыкает - особенно потому, что вы не можете принять порядок исполнения (или это выполнение вообще произойдет).

У вашей проблемы есть простое исправление: просто используйте Seq.iter для принудительного выполнения/оценки последовательности и передайте ей функцию «игнорировать», поскольку нам не нужны значения, возвращаемые последовательностью.

let a = allFiles dir 
    |> Seq.filter(matchFunc) 
    |> Seq.map(addNumber) 
    |> Seq.iter ignore // Forces the sequence to execute 
+1

Это, вероятно, должно быть 'Seq.iter ignore', так как' iter' ожидает функцию. – Daniel

+0

СПАСИБО ВАС !!!!!! – nati

+0

@ Даниэль. Вы абсолютно правы. Теперь это исправлено. –

1

Это общее поведение ленивой последовательности. вы имеете то же самое, скажем, C#, используя IEnumerable, для которого seq является псевдонимом. В псевдокоде:

var lazyseq = "abcdef".Select(a => print a); //does not do anything 
    var b = lazyseq.ToArray(); //will evaluate the sequence 

ToArray запускает оценку последовательности:

Это иллюстрирует тот факт, что последовательность является лишь описанием, и не сказать вам, когда это будет перечислено: это находится под контролем потребителя последовательности.


Чтобы пойти немного дальше на эту тему, вы можете захотеть взглянуть на this page from F# wikibook:

let isNebraskaCity_bad city = 
    let cities = 
     printfn "Creating cities Set" 
     ["Bellevue"; "Omaha"; "Lincoln"; "Papillion"] 
     |> Set.ofList 

    cities.Contains(city) 

let isNebraskaCity_good = 
    let cities = 
     printfn "Creating cities Set" 
     ["Bellevue"; "Omaha"; "Lincoln"; "Papillion"] 
     |> Set.ofList 

    fun city -> cities.Contains(city) 

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

+0

спасибо всем! его первый раз публиковать вопрос здесь, и я не ожидал получить ответы так быстро! :) Кто-нибудь знает ответ на мой последний вопрос? – nati

+0

Литерал действительно будет иметь двоичное представление своего значения, закодированного в байт-коде. это не переменная, а реальная константа, например, PI. и имя PI является просто ярлыком для этого жестко закодированного значения. – nicolas

+0

@ user2088397: Я добавил код в свой ответ, чтобы продемонстрировать статическую переменную. – Daniel

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

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