Я копаюсь в исходном коде F # недавно.
в Seq.fs:
// Binding.
//
// We use a type defintion to apply a local dynamic optimization.
// We automatically right-associate binding, i.e. push the continuations to the right.
// That is, bindG (bindG G1 cont1) cont2 --> bindG G1 (cont1 o cont2)
// This makes constructs such as the following linear rather than quadratic:
//
// let rec rwalk n = { if n > 0 then
// yield! rwalk (n-1)
// yield n }
После просмотра кода выше я протестировал два кода:
let rec rwalk n = seq { if n > 0 then
yield n
yield! rwalk (n-1)
}
и
let rec rwalk n = seq { if n > 0 then
yield! rwalk (n-1)
yield n
}
Я обнаружил, что первый очень быстрый, а второй очень медленный.Если n = 10000, на моей машине стоит 3 секунды, чтобы сгенерировать эту последовательность, таким образом, квадратичное время.
Квадратичное время является разумным, например,
seq { yield! {1; 2; ...; n-1}; yield n }
переводится в
Seq.append {1; 2; ...; n-1} {n}
Эта операция добавления, как я полагаю, должна занимать линейное время.В первом коде операция добавления выглядит следующим образом: seq { yield n; yield! {n-1; n-2; ...; 1} }
, что стоит постоянного времени.
Комментарии в коде говорят, что это linear
(возможно, это линейное не линейное время).Возможно, это linear
относится к использованию настраиваемой реализации для последовательности, а не к вычислительному выражению Moand / F # (как упомянуто в спецификации F #, однако в спецификации не указывается причина для этого ...).
Может кто-нибудь прояснить здесь нечеткость?Большое спасибо!
(ps, поскольку это проблема дизайна и оптимизации языка, я также прикрепил тег Haskell, чтобы увидеть, есть ли у людей понимание.)