2015-05-08 4 views
0

Это вопрос о более раннем классе Persistence, который я пытался выставить в качестве перечислителя. Я понял, что мне нужно передать ссылку действительно, чтобы изменить значение объекта, который я пытаюсь заполнить. Наверное, я собираюсь сделать это на C++-пути (поскольку большинство, возможно, догадалось, что я начинаю F #). Тем не менее, я хочу быть настолько эффективным с точки зрения печати в памяти, насколько смогу. В идеале я хотел бы повторно использовать один и тот же объект снова и снова, когда читаю из файла.IEnumerator in F # continue

У меня возникла проблема с этим кодом, где он не позволяет мне передавать по ссылке в вызове функции serialize. Я снова воспроизвожу код здесь. Я заранее благодарю вас за вашу помощь.

Я получаю ошибку:

error FS0001: This expression was expected to have type byref<'T> but here has type 'T

Если изменить вызов serialize(& current_, reader_) я получаю следующее сообщение об ошибке:

persistence.fs(71,6) : error FS0437: A type would store a byref typed value. This is not permitted by Common IL.
persistence.fs(100,29) : error FS0412: A type instantiation involves a byref type. This is not permitted by the rules of Common IL.
persistence.fs(100,30) : error FS0423: The address of the field current_ cannot be used at this point

КОД:

type BinaryPersistenceIn<'T when 'T: (new : unit -> 'T)>(fn: string, serializer: ('T byref * BinaryReader) -> unit) = 
let stream_ = File.Open(fn, FileMode.Open, FileAccess.Read) 
let reader_ = new BinaryReader(stream_) 
let mutable current_ = new 'T() 

let eof() = 
    stream_.Position = stream_.Length 


interface IEnumerator<'T> with 

    member this.Current 
     with get() = current_ 

    member this.Dispose() = 
     stream_.Close() 
     reader_.Close() 

interface System.Collections.IEnumerator with 

    member this.Current 
     with get() = current_ :> obj 

    member this.Reset() = 
     stream_.Seek((int64) 0., SeekOrigin.Begin) |> ignore 

    member this.MoveNext() = 
     let mutable ret = eof() 
     if stream_.CanRead && ret then 
      serializer(current_, reader_) 

     ret 
+0

Вам действительно нужно иметь это как класс? Как насчет моего предложения с выражением вычисления 'seq' из вашего предыдущего вопроса? http://stackoverflow.com/a/30128698/180286 –

+0

AAH Я не заметил вашего подхода, ориентированного на последовательность ... Позвольте мне проверить это. –

+0

@Fyodor, причина, по которой я не использовал идею stream/sequence, заключается в том, что я не хотел выделять структуру каждый раз. Я читаю базу данных тика, и с течением времени она замедляет чтение. –

ответ

1

Вы можете обойти это путем введения изменчивого локального, передавая его serialize, а затем присваивая current_:

member this.MoveNext() = 
    let mutable ret = eof() 
    if stream_.CanRead && ret then 
     let mutable deserialized = Unchecked.defaultof<_> 
     serializer(&deserialized, reader_) 
     current_ <- deserialized 

    ret 

Но теперь это становится действительно, действительно тревожащий. Обратите внимание на использование Unchecked.defaultof<_>? Нет другого способа инициализировать значение неизвестного типа, и он называется «unchecked» по какой-либо причине: компилятор не может гарантировать безопасность этого кода.

Я настоятельно рекомендую вам изучить другие способы достижения вашей первоначальной цели, например, используя выражение вычисления seq, так как у меня есть suggested in your other question.

+0

Мне нравится ваше выражение последовательности. Но я заметил, что с моим читателем C#, который выделяет структуру для де-сериализации, он замедляется со временем. –

+0

Я предполагаю, что с struct, на которую вы ссылаетесь, является '' T' (которая может быть структурой). Теперь я нахожу это немного странным, так как вы неявно создаете новую структуру при вызове Current as a struct в .NET, имеет семантику значений. Это означает, что Current создаст копию. Для меня было бы более разумным, которое замедлялось бы с течением времени, если бы вы распределяли все больше «классов», поскольку они являются «кучными» объектами и, следовательно, нуждаются в GC: ing. – FuleSnabel

+0

Основная проблема с 'Unchecked.defaultof <>' заключается в том, что она вернет значения «null» для типов ссылок F #, которые не допускают нулевые литералы. Это не так, как «плохо», как «FormatterServices.GetUninitializedObject», который возвращает нестроенный объект. – FuleSnabel

0

Что касается объема памяти, давайте проанализируем вариант последовательности:

  • У вас есть экземпляр seq. Это будет какой-то класс, реализующий IEnumerable<'T>. Это будет проведено до тех пор, пока вам не понадобится seq, то есть не будет перераспределяться каждый раз.
  • Вы занимаете Stream как часть seq с одинаковым сроком службы.
  • У вас есть BinaryReader как часть seq с одинаковым сроком службы.
  • eof : unit -> bool - это класс функций, сгенерированный компилятором как часть seq с одинаковым сроком службы.
  • Петля будет использовать bool для цикла while и состояния if. Оба из них представляют собой структуры, распределенные по стекам и необходимые для логики ветвления.
  • И, наконец, вы yield экземпляр, который вы уже получили из сериализатора.

Концептуально, это так же малое потребление памяти, как и у лениво оцененного seq. Как только элемент потребляется, его можно собрать мусором. Множественные оценки будут делать то же самое снова.

Единственное, с чем вы можете играть, это то, что возвращает сериализатор.

Если у вас есть сериализатор, возвращающий структуру, он копируется и распределяется по стеклу. И это не должно быть изменчивым. Отвратимые структуры обескуражены. Why are mutable structs “evil”?

Структуры хороши в отношении сборщика мусора, поскольку они избегают сбора мусора. Но они, как правило, используются с очень маленькими объектами размером порядка 16-24 байта.

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

Если вы хотите, чтобы связанный с сериализатором побочный эффект, то есть перезапись одного и того же объекта (для этого должен использоваться класс, то есть ссылочный тип), тогда весь подход IEnumerable<'T> и, следовательно, seq является неправильным. IEnumerable s всегда дает вам новые объекты в качестве результата и никогда не должен изменять какой-либо ранее существовавший объект. Единственное состояние с ними должно быть информацией, в каком месте в перечислении они есть.

Так что если вам нужна побочная версия, вы можете сделать что-то вроде (псевдокода).

let readAndOverwrite stream target = 
    let position = // do something here to know the state 
    fun target -> 
     target.MyProp1 <- stream.ReadInt() 
     target.MyProp2 <- stream.ReadFloat() 

Переходя, как byref не кажется очень разумным мне, как вы потом все равно выделить и собирать мусор объект. Таким образом, вы можете так же делать это неизменно. Что вы можете сделать, это просто изменение свойств на вашем объекте.

+0

Я думаю, что вы ошибаетесь в отношении жизни потока. Он будет создан заново для каждого нового перечисления. Что точно соответствует реализации на основе класса OP. Вы можете думать о 'seq' как о соответствующем классу (со связанными конструкторскими аргументами) и о том, что перечисляющие его производятся как соответствующие экземплярам класса. –

+0

@ Даниэль Фабиан, Спасибо за ваши комментарии и уточнения. Я рассмотрю ваше предложение readAndOverwrite. Мне это нравится и кажется очень изящным. Поэтому вы предлагаете мне вернуть функцию. Учитывая, что я начинающий программист F # и функциональный программист. Это будет немного, прежде чем я перевариваю ваше решение и реализую его. –

+0

@ Даниэль Фабиан, я прочитал статью «Почему изменчивые структуры злы?» и г-на Липперса. Я действительно думаю, что его вывод ошибочен. Вместо того, чтобы защищать при использовании структур, брендинг их как зла - не правильный подход.Я не понимаю понятия не изменяемой структуры. Что это? Структура vs класс/тип в моем сознании (простите мою перспективу C++) состоит в том, что у вас есть инкапсуляция, а другая нет. Границу нельзя нарушать. –