У меня есть попытка изучить F#. Я - полный новичок, таким образом, это могло бы быть walkover для Вас парни :)
У меня есть следующая функция:
let removeEven l =
let n = List.length l;
let list_ = [];
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
let list_ = list_ @ [x];
list_;
Это берет список, и возвратите новый список, содержащий все числа, который помещается в нечетный индекс в исходном списке, таким образом, removeEven [x1;x2;x3] = [x1;x3]
Однако я получаю свое уже любимое сообщение об ошибке: Incomplete construct at or before this point in expression...
Если я добавляю печать в конец строки, вместо list_
:
...
print_any list_;
проблема решена. Но я не хочу печатать список, я хочу возвратить его!
Что вызывает это? Почему я не могу возвратить свой список?
Чтобы ответить на ваш Во-первых, компилятор жалуется, потому что есть проблема внутри цикла 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_
не будет доступно из любого кода). Вы можете использовать изменяемую
переменную для исправления вашего кода:
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
Здесь, состояние, используемое во время агрегации, - это логический флаг, указывающий, следует ли включать следующий элемент (на каждом шаге мы меняем флаг, возвращая , а не флаг
). Второй элемент - это список, агрегированный на данный момент (мы добавляем элемент по el :: res
только тогда, когда установлен флаг
. После возврата fold
мы используем snd
, чтобы получить второй элемент кортежа (агрегированный список) и обратить его, используя List.rev
, потому что он был собран в обратном порядке (это более эффективно, чем добавление к end, используя res @ [el]
).
Изменить: Если я правильно понимаю ваши требования, вот версия вашей функции, выполненная в функциональном, а не в императивном стиле, которая удаляет элементы с нечетными индексами.
let removeEven list =
list
|> Seq.mapi (fun i x -> (i, x))
|> Seq.filter (fun (i, x) -> i % 2 = 0)
|> Seq.map snd
|> List.ofSeq
> removeEven ['a'; 'b'; 'c'; 'd'];;
val it : char list = ['a'; 'c']
Еще одна альтернатива, которая (по моим подсчетам) немного медленнее, чем у Джоэла, но короче :)
let removeEven list =
list
|> Seq.mapi (fun i x -> (i, x))
|> Seq.choose (fun (i,x) -> if i % 2 = 0 then Some(x) else None)
|> List.ofSeq
Думаю, это то, что вы ищете.
let removeEven list =
let maxIndex = (List.length list) - 1;
seq { for i in 0..2..maxIndex -> list.[i] }
|> Seq.toList
Тесты
val removeEven : 'a list -> 'a list
> removeEven [1;2;3;4;5;6];;
val it : int list = [1; 3; 5]
> removeEven [1;2;3;4;5];;
val it : int list = [1; 3; 5]
> removeEven [1;2;3;4];;
val it : int list = [1; 3]
> removeEven [1;2;3];;
val it : int list = [1; 3]
> removeEven [1;2];;
val it : int list = [1]
> removeEven [1];;
val it : int list = [1]
Вы можете попробовать подход сопоставления с образцом. Я давно не использовал F # и не могу сейчас что-то протестировать, но это будет примерно так:
let rec curse sofar ls =
match ls with
| even :: odd :: tl -> curse (even :: sofar) tl
| even :: [] -> curse (even :: sofar) []
| [] -> List.rev sofar
curse [] [ 1; 2; 3; 4; 5 ]
Это рекурсивно отбирает четные элементы. Я думаю. Хотя, наверное, я бы использовал подход Джоэла Мюллера. Я не помню, есть ли функция фильтра
на основе индекса, но это, вероятно, было бы идеальным вариантом для использования или создания, если его нет в библиотеках.
Но в целом списки на самом деле не являются предметами индексного типа. Вот для чего нужны массивы. Если вы подумаете, для какого типа алгоритма потребуется список с удаленными четными элементами, возможно, что на этапах, предшествующих этому требованию, элементы могут быть объединены в пары в кортежи, например:
[ (1,2); (3,4) ]
Это сделало бы тривиальным для получить четные «проиндексированные» элементы:
thelist |> List.map fst // take first element from each tuple
Существует множество вариантов, если не гарантируется, что входной список содержит четное количество элементов.