Возвращаемое значение в F# - неполная конструкция

У меня есть попытка изучить 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_;

проблема решена. Но я не хочу печатать список, я хочу возвратить его!

Что вызывает это? Почему я не могу возвратить свой список?

7
задан Frederik Wordenskjold 9 March 2010 в 23:56
поделиться

5 ответов

Чтобы ответить на ваш Во-первых, компилятор жалуется, потому что есть проблема внутри цикла 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] ).

6
ответ дан 6 December 2019 в 15:20
поделиться

Изменить: Если я правильно понимаю ваши требования, вот версия вашей функции, выполненная в функциональном, а не в императивном стиле, которая удаляет элементы с нечетными индексами.

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']
6
ответ дан 6 December 2019 в 15:20
поделиться

Еще одна альтернатива, которая (по моим подсчетам) немного медленнее, чем у Джоэла, но короче :)

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
0
ответ дан 6 December 2019 в 15:20
поделиться

Думаю, это то, что вы ищете.

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]
2
ответ дан 6 December 2019 в 15:20
поделиться

Вы можете попробовать подход сопоставления с образцом. Я давно не использовал 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

Существует множество вариантов, если не гарантируется, что входной список содержит четное количество элементов.

0
ответ дан 6 December 2019 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: