Я недавно врывался в исходный код 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 #, однако в спецификации не упоминается причина для этого...).
Может ли кто-нибудь прояснить эту нечеткость? Большое спасибо!
(p.s. потому что это проблема проектирования и оптимизации языка, я также добавил тег Haskell, чтобы увидеть, есть ли у людей информация.)