2010-03-09 11 views
7

Intento aprender F #. Soy un principiante, por lo que esta podría ser una victoria fácil para ustedes :)Valor de retorno en F # - constructo incompleto

tengo la siguiente función:

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_; 

Toma una lista y devuelve una nueva lista que contiene todos los números, que se coloca en un índice impar en la lista original, por lo removeEven [x1;x2;x3] = [x1;x3]

Sin embargo, me sale mi ya favorita de mensajes de error: Incomplete construct at or before this point in expression...

Si añado una impresión hasta el final de la línea, en lugar de list_:

... 
print_any list_; 

El problema es arreglado. Pero no quiero imprimir la lista, quiero devolver!

¿Qué causa esto? ¿Por qué no puedo devolver mi lista?

Respuesta

6

Para responder a su pregunta en primer lugar, el compilador se queja de que hay un problema dentro del bucle for. En F #, let sirve para declarar valores (que son inmutables y no pueden ser modificados más adelante en el programa). No es una declaración como en C# - let solo se puede usar como parte de otra expresión. Por ejemplo:

let n = 10 
n + n 

en realidad significa que desea que el símbolo n para referirse al valor 10 en la expresión n + n. El problema con el código es que usted está utilizando let sin ningún tipo de expresión (probablemente debido a que desea utilizar variables mutables):

for x in seq_ do 
    let list_ = list_ @ [x] // This isn't assignment! 
list_ 

la línea problemática es una expresión incompleta - let utilizando de esta manera no se permite , porque no contiene ninguna expresión (no se accederá al valor list_ desde ningún código). Puede utilizar mutable variable para corregir su código:

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 '<-' 

Ahora bien, esto debería funcionar, pero en realidad no es funcional, porque usted está utilizando la mutación imprescindible. Además, agregar elementos usando @ es algo realmente ineficiente en lenguajes funcionales. Por lo tanto, si desea que su código sea funcional, probablemente necesite usar un enfoque diferente. Ambas respuestas muestran un gran enfoque, aunque prefiero el ejemplo de Joel, porque la indexación en una lista (en la solución de Chaos) tampoco es muy funcional (no hay aritmética de puntero, por lo que también será más lenta) .

Probablemente la solución funcional más clásico sería el uso de la función List.fold, que agrega todos los elementos de la lista en un único resultado, caminando desde la izquierda a la derecha:

[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 

Aquí, el estado usa durante la agregación hay una bandera booleana que especifica si se debe incluir el siguiente elemento (durante cada paso, volteamos la bandera al devolver not flag). El segundo elemento es la lista agregada hasta el momento (agregamos elemento por el::res solo cuando se establece flag. Después de que fold regresa, usamos snd para obtener el segundo elemento de la tupla (la lista agregada) y revertirlo usando List.rev, porque se recolectó en el orden inverso (esto es más eficiente que agregarlo al final usando [email protected][el]).

+0

Tamaño de la lista = 1000; Iteraciones = 100; Resultado (ChaosPandion) = 4200ms; Resultado (Joel Mueller) = 150 ms; – ChaosPandion

+0

¿Qué tal el enfoque 'List.fold' de Tomás? –

+0

Llegó a unos 135ms. – ChaosPandion

5

Editar: Si entiendo sus requisitos correctamente, aquí hay una versión de su función hecha estilo funcional en lugar de estilo imperativo, que elimina elementos con índices impares.

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'] 
+0

me gustaría utilizar 'Seq' personalmente. – ChaosPandion

+0

¿Esto no devuelve los números impares en la secuencia, y no los números, colocados en un índice impar en la matriz? (editaré la publicación original para que quede más claro). –

+0

Podría ser mejor, pero quería hacer coincidir la firma del método previsto de '(int list -> int list)'. Tal vez hay una razón por la que Frederik quiere la salida como una lista en lugar de una secuencia. –

2

Creo que esto es lo que está buscando.

let removeEven list = 
    let maxIndex = (List.length list) - 1; 
    seq { for i in 0..2..maxIndex -> list.[i] } 
    |> Seq.toList 

Pruebas de

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] 
0

Puede intentar un enfoque de coincidencia de patrones. No he usado F # por un tiempo y no puedo probar las cosas correctamente ahora, pero sería algo como esto:

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 ] 

Esto recoge recursivamente el elemento par ts. Creo. Sin embargo, probablemente usaría el enfoque de Joel Mueller. No recuerdo si hay una función basada en índice filter, pero probablemente sería la ideal para usar, o para hacer si no existe en las bibliotecas.

Pero en general, las listas no son realmente un tipo de cosas de índice. Para eso están las matrices. Si se tiene en cuenta qué tipo de algoritmo requeriría una lista con sus incluso elementos eliminados, tal vez es posible que en los pasos previos a este requisito, los elementos pueden ser emparejados en tuplas, como esto:

[ (1,2); (3,4) ] 

Eso haría que sea trivial para obtener los elementos Even- "indexados" salida:

thelist |> List.map fst // take first element from each tuple 

Hay una variedad de opciones si la lista de entrada no está garantizado para tener un número par de elementos.

0

Otra alternativa, que (según mis cálculos) es ligeramente más lento que el de Joel, pero es más corto :)

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