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/2
tienen 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.
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
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. –
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