2011-09-24 5 views
9

¿Cómo hago una operación para cada elemento de una lista, en orden?ejecutando la operación para cada elemento de lista en swi-prolog y otros

Sobre la base de estos dos recursos:

  1. http://www.swi-prolog.org/pldoc/doc/swi/library/lists.pl
  2. http://www.swi-prolog.org/pldoc/doc_for?object=foreach/2

Imagino que siempre se puede confiar en:

  • foreach(member(X, [1,2]), write(X)).

¿Es eso determinista y puedo envolver el predicado miembro/2 como me plazca en mis propios predicados y aún así iterar siempre en orden?

Respuesta

16

Sí, pero debe preocuparse por la falla de su predicado. Si puede, los elementos restantes en la lista no se procesarán porque produce una conjunción en lugar de un bucle impulsado por falla.

Yo estaría más dispuesto a usar maplist/2 ya que creo que es más utilizado que foreach/2 pero tampoco había visto esta opción antes. :)

Editar: Veamos lo que quiero decir sobre los bucles impulsados ​​por fallas.

Hay dos métodos de iteración primitivos en Prolog: recursividad y ciclos impulsados ​​por fallas. Digamos que quiero imprimir cada elemento en una lista. El método recursivo va a tener este aspecto:

print_all([]). 
print_all([X|Rest]) :- write(X), nl, print_all(Rest). 

Así da una lista como [1,2,3], esto va a ampliar de este modo:

print_all([1,2,3]) 
    write(1), nl, print_all([2,3]) 
    write(1), nl, write(2), nl, print_all([3]) 
     write(1), nl, write(2), nl, write(3), nl, print_all([]) 
     write(1), nl, write(2), nl, write(3), nl. 

Esta es la forma en member/2 se implementa normalmente:

member(X, [X|_]). 
member(X, [_|Xs]) :- member(X, Xs). 

Así que puede ver que el método recursivo es bastante simple y general.

Otro método simple pero algo desaprobado es simular una falla al activar el mecanismo de retroceso. Esto se llama un bucle de fallo impulsada y se ve así:

print_all(List) :- member(X, List), write(X), nl, fail. 
print_all(_). 

Cuando se ejecuta esta versión de print_all/1, lo que pasa es un poco más compleja que la simple expansión.

print_all([1,2,3]) 
    member([1,2,3], 1) 
    write(1), nl 
     fail 
    retry member([1,2,3], 2) 
    write(2), nl 
     fail 
    retry member([1,2,3], 3) 
    write(3), nl 
     fail 
retry print_all(_) 
    true 

verbalmente, las fuerzas fail Prolog con espalda hasta el último punto de elección se hizo e intente utilizar el siguiente solución. Bueno, write/1 y nl/0 no producen puntos de elección porque solo tienen una solución, pero member/2tienen tienen varias soluciones, una para cada elemento de la lista. Entonces, Prolog saca cada elemento de la lista y lo imprime.Finalmente cuando member/2 se queda sin soluciones, Prolog retrocede al punto de elección anterior, que es el segundo cuerpo del predicado print_all/1, que siempre tiene éxito. Entonces, el resultado es el mismo. Creo que la gente hoy en día generalmente prefiere no usar ciclos impulsados ​​por fallas, pero no entiendo los argumentos lo suficientemente bien como para repetirlos de manera útil.

Una cosa que puede ayudarlo a ver qué está pasando es utilizar el predicado trace y recorrer la expansión de ambas versiones, y ver si puede dar sentido a las diferencias. Mi notación anterior está completamente compuesta para esta respuesta y puede no ser tan clara.

Mirando hacia atrás en lo que originalmente escribí y su pregunta real:

  • foreach va a ser determinista
  • member siempre se repetirá en orden, porque las listas se definen de tal manera que debe tener acceso cada elemento a su vez

Por otra parte, estos días al menos en SO obtendrá mucha gente diciéndole que use maplist y su estilo, por lo que probablemente no solo va a funcionar, sino también una buena idea.

+0

Veo así construir una lista con un bucle impulsado por fallas del predicado donde quiero todas las soluciones, luego la lista de mapas sobre el bucle para imprimirlas. Creo que necesitará un bucle controlado por fallas en alguna parte porque mi programa es muy mucho basado en pruebas en lugar de basado en cálculos. – codeshot

+0

Siempre debería ser posible hacerlo de cualquier manera. Recomiendo evitar el ciclo impulsado por fallas si puede, pero si tiene más sentido, úselo. –

+0

Mi lectura de la documentación de maplist/2 es que la lista puede ser reordenada, lo que significa que las acciones se ejecutarán en orden arbitrario. Eso significa que no resuelve el problema. La recursividad tiene la complejidad añadida de no describir mis intenciones, pero creo que foldl/4 implementa lo que necesito de una manera razonablemente descriptiva si solo proporciono un calce alrededor de la aplicación que acepta dos parámetros adicionales para no acumular nada: actuar (A, _, _): call (A) – codeshot

Cuestiones relacionadas