2011-08-03 10 views
5

Estoy buscando una manera de encontrar tuplas en una lista en Erlang usando una tupla parcial, de manera similar a los funtores que coinciden en Prolog. Por ejemplo, me gustaría siguiente código para volver true:Tuplas coincidentes con variables "no importa" en Erlang

member({pos, _, _}, [..., {pos, 1, 2}, ...]) 

Este código no funciona de inmediato debido a los siguientes errores:

variable '_' is unbound 

¿Hay un breve camino para lograr el mismo efecto ?

Respuesta

3

Para casos simples, es mejor utilizar el ya mencionado lists:keymember/3. Pero si realmente necesita member función se puede implementar por sí mismo de esta manera:

member(_, []) -> 
    false; 
member(Pred, [E | List]) -> 
    case Pred(E) of 
     true -> 
      true; 
     false -> 
      member(Pred, List) 
    end. 

Ejemplo:

>>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]). 
+0

Esperaba evitar esto, pero parece que una función de predicado es la única forma de pasar un patrón como argumento. –

+0

'listas: any' en realidad hace exactamente lo mismo que esta función' miembro' :) – legoscia

+0

@legoscia Tienes razón :-) – hdima

0

que podría hacerlo usando listas por comprensión:

Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].

+0

Eso es bueno, pero puede ser re -written para que el patrón se dará como un argumento de función (por ejemplo, para escribir una función "my_member")? –

+0

Puedes escribirlo aún más simple '[Match || {pos, _, _} = Coincide con <- ZeList] 'pero es la funcionalidad _filter_ not _member_. – hdima

+0

@Little Bobby Tables [Elem || Elem <- List, my_member (Elem)] pero puede hacer lo mismo con las listas: filter (fun my_member/1, List) – hdima

3

Uso lists:keymember/3 lugar.

+0

¿Esto también permitiría la coincidencia de patrones? Como la pregunta no es acerca de obtener una coincidencia de igualdad. –

+0

@WardB 'listas: keymember (pos, 1, List)' por ejemplo en la pregunta – hdima

0

Otra posibilidad sería la de hacer lo que las especificaciones partido de hacer y usar el átomo '_' en lugar de una prima _. Entonces, se podría escribir una función similar a la siguiente:

member(X, List) when is_tuple(X), is_list(List) -> 
    member2(X, List). 

% non-exported helper functions: 

member2(_, []) -> 
    false; 
member2(X, [H|T]) when not is_tuple(H); size(X) =/= size(H) -> 
    member2(X, T); 
member2(X, [H|T]) -> 
    case is_match(tuple_to_list(X), tuple_to_list(H)) of 
     true -> true; 
     false -> member2(X, T) 
    end. 

is_match([], []) -> 
    true; 
is_match(['_'|T1], [_|T2]) -> 
    is_match(T1, T2); 
is_match([H|T1], [H|T2]) -> 
    is_match(T1, T2); 
is_match(_, _) -> 
    false. 

entonces, su llamada sería ahora:

member({pos, '_', '_'}, [..., {pos, 1, 2}, ...]) 

Esto no dejaría que coinciden con los patrones como {A, A, '_'} (comprobando que los dos primeros los elementos son idénticos), pero si no necesitas variables, esto debería funcionar.

También podría extenderlo a utilizar variables utilizando una sintaxis similar para que coincida con las especificaciones ('$1', '$2', etc) con un poco más de trabajo - agregar un tercer parámetro a is_match con las asociaciones de variables que hemos visto hasta ahora, luego escribe cláusulas de función para ellos similares a la cláusula para '_'.

De acuerdo, este no será el método más rápido. Con la advertencia que realmente no he medido, espero que usar la coincidencia de patrones en el lenguaje utilizando una función divertida proporcione un rendimiento mucho mejor, aunque hace que el sitio de llamadas sea un poco más detallado. Es un compromiso que tendrás que considerar.

2

Puede hacerlo con una macro utilizando una lista por comprensión:

-define(member(A,B), length([0 || A <- B])>0). 

?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]). 

No es muy eficiente (que corre a través de toda la lista), pero es lo más cerca que puedo pensar a la sintaxis originales.

Si desea extraer realmente los elementos que coinciden con que acaba de quitar 'longitud' y agrega una variable:

-define(filter(A,B), [_E || A =_E <- B]). 
0

prohibido utilizar ets:match:

6> ets:match(T, '$1'). % Matches every object in the table 
[[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]] 
7> ets:match(T, {'_',dog,'$1'}). 
[[7],[5]] 
8> ets:match(T, {'_',cow,'$1'}). 
[]