2012-01-16 1 views
1

У меня есть последовательность делителей простых чисел, которую я хочу перебрать для каждого основного кандидата. Я использую GetEnumerator() MoveNext() и Current. Я не могу повторно инициализировать счетчик, чтобы начать с самого начала. Я попытался скомпилировать Reset(), который скомпилировал, но дает ошибку выполнения, которая не реализована.F #: хотите повторно инициализировать перечислитель

Я использую F # 2.0 Интерактивное построить 4.0.40219.1

Любые предложения?

С уважением, Doug


Для выяснения вопроса: Для каждого простого кандидата NI хочет итерацию через премьер последовательность делителей (до приблизительно квадратного корня N) и полностью фактор N или определить, является ли премьер , Используя GetEnumerator, MoveNext, Текущий подход работает для первого основного кандидата, но на втором премьер-кандидате я хочу итерации по моей последовательности делителей с самого начала. Похоже, что единственный способ сделать это - создать новый итератор (что неудобно для большого числа простых кандидатов) или создать новую премьер-последовательность (которую я не хочу делать).

Попытка использовать что-то вроде «делителей в seqPrimes», кажется, исчерпывает все делители перед остановкой, но я хочу остановиться, как только простой делитель делит основного кандидата.

Если в приведенных выше утверждениях есть ошибка в моей логике, сообщите мне.

Я исследовал Seq.cache, и это сработало для меня. Полученный код следующим образом:


// Recursive isprime function (modified from MSDN) 
let isPrime n = 
    let rec check i = 
     i > n/2 || (n % i <> 0 && check (i + 2)) 
    if n = 2 then true 
    elif (n%2) = 0 then false 
    else check 3 

let seqPrimes = seq { for n in 2 .. 100000 do if isPrime n then yield n } 

// Cache the sequence to avoid recomputing the sequence elements. 
let cachedSeq = Seq.cache seqPrimes 


// find the divisors of n (or determine prime) using the seqEnum enumerator 
let rec testPrime n (seqEnum:System.Collections.Generic.IEnumerator<int>) = 
    if n = 1 then printfn "completely factored" 
    else 
    let nref = ref n 
    if seqEnum.MoveNext() then 
     let divisor = seqEnum.Current 
     //printfn "trial divisor %A" divisor 
     if divisor*divisor > n then printfn "prime %A" !nref 
     else 
     while ((!nref % divisor) = 0) do 
      printfn "divisor %A" divisor 
      nref := !nref/divisor 
     testPrime !nref seqEnum 

// test 
for x = 1000000 to 1000010 do 
    printfn "\ndivisors of %d = " x 
    let seqEnum = cachedSeq.GetEnumerator() 
    testPrime x seqEnum 
    seqEnum.Dispose() // not needed 
+2

Мне бы очень редко приходилось явно обращаться к членам на «IEnumerator», когда вы могли просто использовать синтаксис 'for x in y'. Если вам нужно; просто создайте новый счетчик. Проводка кода будет полезна, потому что я думаю, что мы можем найти лучшую альтернативу. – vcsjones

+1

Можете ли вы привести конкретный пример, демонстрирующий ошибку? – pad

+0

Вы пытались использовать Seq.cache? – Huusom

ответ

3

Если вы имеете в виду, что причина вашей попытки сбросить Enumerator является высокой стоимостью регенерировать вашу последовательность простых чисел, вы можете рассмотреть caching вашей последовательности. Этот способ использования вашей последовательности будет идиоматичным для F #. Для того, чтобы показать вам, как сделать это я имею в виду вас следующий фрагмент взят из this context:

let rec primes = 
    Seq.cache <| seq { yield 2; yield! Seq.unfold nextPrime 3 } 
and nextPrime n = 
    if isPrime n then Some(n, n + 2) else nextPrime(n + 2) 
and isPrime n = 
    if n >= 2 then 
     primes 
     |> Seq.tryFind (fun x -> n % x = 0 || x * x > n) 
     |> fun x -> x.Value * x.Value > n 
    else false 

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

Говоря о Reset() метод IEnumerator, я помню, что он не реализован в текущем F #, то есть выбрасывает System.NotSupportedException. См. MSDN reference для обоснования.

Сложение: Для того, чтобы проверить его с помощью теста вы предложили ниже:

for x in [1000000..1000010] do 
    printfn "\ndivisors of %d" x 
    primes 
    |> Seq.takeWhile ((>) (int(sqrt(float x)))) 
    |> Seq.iter (fun n -> if x%n = 0 then printf "%d " n) 

На моем выполнении теста ноутбук занимает считанные 3 мс.

+0

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

+0

Я использовал «Ответ на ваш вопрос» и отправил его, но он пока не появился. – DougT

+1

@DougT: Правильно, сброс перечислителя эквивалентен созданию нового счетчика; последнее является нормальным и идиоматическим в F #. Использование кассовой последовательности позволит вам не платить штраф за выполнение этого курса действий. Вы можете играть с кодом выше в 'fsi', чтобы убедиться сами. –

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

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