Чтобы ответить на ваш вопрос первым, компилятор жалуется, потому что внутри цикла for
есть проблема. В F # let
служит для объявления значений (которые неизменны и не могут быть изменены позже в программе). Это не оператор, поскольку в C # - let
может использоваться только как часть другого выражения. Например:
let n = 10
n + n
Фактически означает, что вы хотите, чтобы символ n
ссылался на значение 10
в выражении n + n
. Проблема с вашим кодом в том, что вы используете let
без каких-либо выражений (возможно, потому что вы хотите использовать изменяемые переменные):
for x in seq_ do
let list_ = list_ @ [x] // This isn't assignment!
list_
Проблемная строка является неполным выражением - использование let
таким способом недопустимо, поскольку оно не содержит никакого выражения (значение list_
не будет доступно из любого кода). Вы можете использовать mutable
переменную для исправления вашего кода:
let mutable list_ = [] // declared as 'mutable'
let seq_ = seq { for x in 1..n do if x % 2 <> 0 then yield List.nth l (x-1)}
for x in seq_ do
list_ <- list_ @ [x] // assignment using '<-'
Теперь, это должно работать, но это не совсем функционально, потому что вы используете императивную мутацию. Более того, добавление элементов с использованием @
на функциональных языках действительно неэффективно. Итак, если вы хотите сделать свой код функциональным, вам, вероятно, придется использовать другой подход. Оба других ответа демонстрируют отличный подход, хотя я предпочитаю пример Джоэла, потому что индексирование в список (в решении от Chaos) также не очень функционально (нет арифметики с указателями, поэтому она будет также медленнее) .
Вероятно, наиболее классическим функциональным решением было бы использование функции List.fold
, которая объединяет все элементы списка в один результат, проходя слева направо:
[1;2;3;4;5]
|> List.fold (fun (flag, res) el ->
if flag then (not flag, el::res) else (not flag, res)) (true, [])
|> snd |> List.rev
Здесь состояние, используемое во время агрегации, является логическим флагом, указывающим, следует ли включать следующий элемент (на каждом шаге мы переворачиваем флаг, возвращая not flag
). Второй элемент - это агрегированный до сих пор список (мы добавляем элемент на el::res
, только когда установлен flag
. После возврата fold
мы используем snd
, чтобы получить второй элемент кортежа (агрегированный список) и переверните его, используя List.rev
, потому что он был собран в обратном порядке (это более эффективно, чем добавление в конец с помощью res@[el]
).