There's a better solution than the one described by mat that is faster and avoids spurious choice points but it requires support for the call//1
built-in non-terminal as found on e.g. SWI-Prolog, GNU Prolog, and other Prolog compilers. Also on Logtalk. Consider:
as_opt(Option) --> "--as=", list(Option).
list([L|Ls]) --> [L], list(Ls).
list([]) --> [].
as_opt2(Option) --> "--as=", call(rest(Option)).
rest(Rest, Rest, _).
Using SWI-Prolog to better illustrate the differences:
?- phrase(as_opt(Option), "--as=json").
Option = [106, 115, 111, 110] ;
false.
?- phrase(as_opt2(Option), "--as=json").
Option = [106, 115, 111, 110].
?- time(phrase(as_opt(Option), "--as=json")).
% 9 inferences, 0.000 CPU in 0.000 seconds (57% CPU, 562500 Lips)
Option = [106, 115, 111, 110] ;
% 2 inferences, 0.000 CPU in 0.000 seconds (63% CPU, 133333 Lips)
false.
?- time(phrase(as_opt2(Option), "--as=json")).
% 6 inferences, 0.000 CPU in 0.000 seconds (51% CPU, 285714 Lips)
Option = [106, 115, 111, 110].
The spurious choice-point comes from the definition of the list//1
non-terminal. The performance difference is that, while call//1
allows us to simply access the implicit list argument, the list//1
non-terminal is doing a list copy element by element of that implicit argument. As a consequence, the list//1
version performance is linear on the characters following the --as=
prefix while the call//1
performance is constant, independent of the number of characters after the prefix:
?- time(phrase(as_opt(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
% 54 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 2700000 Lips)
Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...] ;
% 4 inferences, 0.000 CPU in 0.000 seconds (69% CPU, 137931 Lips)
false.
?- time(phrase(as_opt2(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
% 6 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 333333 Lips)
Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...].