1% ---------------------------------------------------------------------- 2% BEGIN LICENSE BLOCK 3% Version: CMPL 1.1 4% 5% The contents of this file are subject to the Cisco-style Mozilla Public 6% License Version 1.1 (the "License"); you may not use this file except 7% in compliance with the License. You may obtain a copy of the License 8% at www.eclipse-clp.org/license. 9% 10% Software distributed under the License is distributed on an "AS IS" 11% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 12% the License for the specific language governing rights and limitations 13% under the License. 14% 15% The Original Code is The ECLiPSe Constraint Logic Programming System. 16% The Initial Developer of the Original Code is Cisco Systems, Inc. 17% Portions created by the Initial Developer are 18% Copyright (C) 1995-2006 Cisco Systems, Inc. All Rights Reserved. 19% 20% Contributor(s): ECRC GmbH 21% Contributor(s): IC-Parc, Imperal College London 22% 23% END LICENSE BLOCK 24% 25% System: ECLiPSe Constraint Logic Programming System 26% Version: $Id: apply_macros.pl,v 1.4 2013/02/09 20:27:57 jschimpf Exp $ 27% ---------------------------------------------------------------------- 28 29:- module(apply_macros). 30 31:- comment(categories, ["Algorithms","Programming Utilities"]). 32:- comment(summary, "Utilities to apply a predicate to all elements of a list resp. all subterms of a term"). 33:- comment(author, "Joachim Schimpf, ECRC Munich"). 34:- comment(copyright, "Cisco Systems, Inc"). 35:- comment(date, "$Date: 2013/02/09 20:27:57 $"). 36:- comment(desc, html(" 37 Note that this library is largely superseded by the do-loop construct! 38 <P> 39 A collection of utilities to apply a predicate to 40 all elements of a list resp. all subterm of a term. 41 To avoid performance degradation due to apply/2, 42 they are implemented as macros, i.e. they are specialized 43 into auxiliary predicates without metacalls, and the 44 calls are translated into calls of the auxiliary predicates.") 45 ). 46 47:- export 48 fromto/4, 49 maplist/3, 50 mapstream/3, 51 mapargs/3, 52 appnodes/2, 53 sumargs/4, 54 checklist/2, 55 applist/2, 56 selectlist/3, 57 sumlist/4, 58 sumnodes/4. 59 60 61:- comment(fromto/4, [template:"fromto(+From, +To, +Step, +Pred)", 62 summary:"Call Pred with the numbers From..To in increments of Step" 63 ]). 64:- comment(maplist/3, [template:"maplist(+Pred, +ListIn, ?ListOut)", 65 summary:"Create new list by applying a predicate to all list elements", 66 eg:"maplist(times(3), [1,2,3,4], [3,6,9,12])." 67 ]). 68:- comment(mapstream/3, [template:"mapstream(+Pred, ?ListIn, ?ListOut)", 69 summary:"Like maplist/3, but delays if ListIn is not complete" 70 ]). 71:- comment(mapargs/3, [template:"mapargs(+Pred, +TermIn, ?TermOut)", 72 summary:"Create new term by applying a predicate to all arguments", 73 eg:"mapargs(atom_string, s(a,b,c), s(\"a\",\"b\",\"c\"))." 74 ]). 75:- comment(appnodes/2, [template:"appnodes(+Pred, +Term)", 76 summary:"Call Pred on all Subterms of Term (depth-first and left-to-right order)" 77 ]). 78:- comment(sumargs/4, [template:"sumargs(+Pred, +Term, ?AccIn, ?AccOut)", 79 summary:"Call Pred on all arguments of Term and collect a result in Accumulator", 80 desc:"The traversal order is unspecified!" 81 ]). 82:- comment(checklist/2, [template:"checklist(+Pred, +List)", 83 summary:"Apply a predicate to all list elements", 84 eg:"checklist(<(0), [1,2,3])." 85 ]). 86:- comment(applist/2, [template:"applist(+Pred, +List)", 87 summary:"Apply a predicate to all list elements", 88 eg:"applist(<(0), [1,2,3])." 89 ]). 90:- comment(selectlist/3, [template:"selectlist(+Pred, +ListIn, ?ListOut)", 91 summary:"Creates output list of all list elements that pass a given test", 92 eg:"selectlist(<(0), [1,0,-2,3], [1,3])." 93 ]). 94:- comment(sumlist/4, [template:"sumlist(+Pred, +List, ?AccIn, ?AccOut)", 95 summary:"Call Pred on all element of List and collect a result in Accumulator", 96 eg:" 97 sumlist(plus, [1,2,3,4], 1, 10). 98 sumlist(times, [1,2,3,4], 1, 24)." 99 ]). 100:- comment(sumnodes/4, [template:"sumnodes(+Pred, +Term, ?AccIn, ?AccOut)", 101 summary:"Call Pred on all Subterms of Term and collect a result in Accumulator", 102 desc:"The traversal is depth-first and left-to-right", 103 eg:" 104 sumnodes(vars, s(1,t(X,2),[Y]), [], [X,Y]). 105 where 106 vars(X, Vars, [X|Vars]) :- var(X), !. 107 vars(_, Vars, Vars). 108 109 or even more elegant using grammar rules: 110 111 sumnodes(vars, s(1,t(X,2),[Y]), [X,Y], []). 112 where 113 vars(X) --> {var(X)} -> [X];[]." 114 ]). 115 116 117:- inline(fromto/4, t_fromto/3). 118:- inline(maplist/3, t_maplist/3). 119:- inline(mapstream/3, t_mapstream/3). 120:- inline(mapargs/3, t_mapargs/3). 121:- inline(appnodes/2, t_appnodes/3). 122:- inline(sumargs/4, t_sumargs/3). 123:- inline(checklist/2, t_checklist/3). 124:- inline(applist/2, t_applist/3). 125:- inline(selectlist/3, t_selectlist/3). 126:- inline(sumlist/4, t_sumlist/3). 127:- inline(sumnodes/4, t_sumnodes/3). 128 129 130t_maplist(maplist(Pred, In, Out), NewGoal, Module) :- 131 callable(Pred), 132 !, 133 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 134 aux_pred_name(maplist, Pattern, NewName), 135 append(VarArgs, [In, Out], NewGoalArgs), % make new goal 136 NewGoal =.. [NewName|NewGoalArgs], 137 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 138 append_args(HeadPrefix, [[], []], NilClause), 139 append_args(HeadPrefix, [[Old|Olds], [New|News]], LoopHead), 140 append_args(GenPred, [Old, New], Apply), 141 append_args(HeadPrefix, [Olds, News], Recursion), 142 compile_aux([ 143 NilClause, 144 (LoopHead :- Apply, Recursion) 145 ], Module). 146 147 148t_fromto(fromto(From, To, Step, Pred), NewGoal, Module) :- 149 callable(Pred), 150 number(Step), 151 !, 152 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 153 aux_pred_name(fromto, (Step,Pattern), NewName), 154 append(VarArgs, [From,To], NewGoalArgs), % make new goal 155 NewGoal =.. [NewName|NewGoalArgs], 156 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 157 append_args(HeadPrefix, [From0, To0], LoopHead), 158 append_args(GenPred, [From0], Apply), 159 append_args(HeadPrefix, [From1,To0], Recursion), 160 ( Step >= 0 -> Cond = (From0=<To0) ; Cond = (From0>=To0) ), 161 compile_aux([ 162 (LoopHead :- 163 ( Cond -> 164 Apply, From1 is From0+Step, Recursion 165 ; 166 true 167 ) 168 ) 169 ], Module). 170 171 172% similar to maplist, but with a delay clause 173 174t_mapstream(mapstream(Pred, In, Out), NewGoal, Module) :- 175 callable(Pred), 176 !, 177 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 178 aux_pred_name(mapstream, Pattern, NewName), 179 append(VarArgs, [In, Out], NewGoalArgs), % make new goal 180 NewGoal =.. [NewName|NewGoalArgs], 181 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 182 append_args(HeadPrefix, [[], []], NilClause), 183 append_args(HeadPrefix, [InStream, _], DelayHead), 184 append_args(HeadPrefix, [[Old|Olds], News], LoopHead), 185 append_args(GenPred, [Old, New], Apply), 186 append_args(HeadPrefix, [Olds, News0], Recursion), 187 compile_aux([ 188 (delay DelayHead if var(InStream)), 189 NilClause, 190 (LoopHead :- Apply, News = [New|News0], Recursion) 191 ], Module). 192 193 194 195% selectlist/3 creates output list of all list elements that pass a given test 196% e.g. selectlist(<(0), [5,-3,0,2,-7], [5, 2]) 197 198t_selectlist(selectlist(Pred, In, Out), NewGoal, Module) :- 199 callable(Pred), 200 !, 201 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 202 aux_pred_name(selectlist, Pattern, NewName), 203 append(VarArgs, [In, Out], NewGoalArgs), % make new goal 204 NewGoal =.. [NewName|NewGoalArgs], 205 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 206 append_args(HeadPrefix, [[], []], NilClause), 207 append_args(HeadPrefix, [[Old|Olds], News], LoopHead), 208 append_args(GenPred, [Old], Apply), 209 append_args(HeadPrefix, [Olds, News0], Recursion), 210 compile_aux([ 211 NilClause, 212 (LoopHead :- 213 (Apply -> News = [Old|News0] ; News = News0), 214 Recursion) 215 ], Module). 216 217 218 219t_applist(applist(Pred, List), NewGoal, Module) :- 220 callable(Pred), 221 !, 222 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 223 aux_pred_name(applist, Pattern, NewName), 224 append(VarArgs, [List], NewGoalArgs), % make new goal 225 NewGoal =.. [NewName|NewGoalArgs], 226 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 227 append_args(HeadPrefix, [[]], NilClause), 228 append_args(HeadPrefix, [[Old|Olds]], LoopHead), 229 append_args(GenPred, [Old], Apply), 230 append_args(HeadPrefix, [Olds], Recursion), 231 compile_aux([ 232 NilClause, 233 (LoopHead :- Apply, Recursion) 234 ], Module). 235 236 237t_checklist(checklist(Pred, List), NewGoal, Module) :- 238 t_applist(applist(Pred, List), NewGoal, Module). 239 240t_sumlist(sumlist(Pred, List, AccIn, AccOut), NewGoal, Module) :- 241 callable(Pred), 242 !, 243 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 244 aux_pred_name(sumlist, Pattern, NewName), 245 append(VarArgs, [List, AccIn, AccOut], NewGoalArgs), % make new goal 246 NewGoal =.. [NewName|NewGoalArgs], 247 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 248 append_args(HeadPrefix, [[], Acc0, Acc0], NilClause), 249 append_args(HeadPrefix, [[Old|Olds], Acc10, Acc12], LoopHead), 250 append_args(GenPred, [Old, Acc10, Acc11], Apply), 251 append_args(HeadPrefix, [Olds, Acc11, Acc12], Recursion), 252 compile_aux([ 253 NilClause, 254 (LoopHead :- Apply, Recursion) 255 ], Module). 256 257 258t_mapargs(mapargs(Pred, In, Out), NewGoal, Module) :- 259 callable(Pred), 260 !, 261 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 262 aux_pred_name(mapargs, Pattern, NewName), 263 append(VarArgs, [In, Out], NewGoalArgs), % make new goal 264 NewGoal =.. [NewName|NewGoalArgs], 265 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 266 append_args(HeadPrefix, [Old, New], TopHead), 267 append_args(HeadPrefix, [_, _, 0], NullHead), 268 append_args(HeadPrefix, [Old, New, N0], LoopHead), 269 append_args(GenPred, [OldArg, NewArg], Apply), 270 append_args(HeadPrefix, [Old, New, N], Recursion), 271 compile_aux([ 272 (TopHead :- 273 functor(Old, F, N), 274 functor(New, F, N), 275 Recursion 276 ), 277 (NullHead :- !), 278 (LoopHead :- 279 arg(N0, Old, OldArg), 280 arg(N0, New, NewArg), 281 N is N0-1, 282 Apply, 283 Recursion) 284 ], Module). 285 286 287% appnodes(Pred, Term) 288% call Pred on all Subterms of Term 289% traversal is depth-first and left-to-right 290 291t_appnodes(appnodes(Pred, Term), NewGoal, Module) :- 292 callable(Pred), 293 !, 294 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 295 aux_pred_name(appnodes, Pattern, NewName), 296 aux_pred_name(appnodes_list, Pattern, NewNameList), 297 append(VarArgs, [Term], NewGoalArgs), % make new goal 298 NewGoal =.. [NewName|NewGoalArgs], 299 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 300 append_args(NewNameList, GenVarArgs, HeadPrefixList), 301 append_args(HeadPrefix, [T], HeadA1), 302 append_args(GenPred, [T], Apply), 303 append_args(HeadPrefixList, [[]], HeadL1), 304 append_args(HeadPrefixList, [[T|Args]], HeadL2), 305 append_args(HeadPrefixList, [T], RecList), 306 append_args(HeadPrefixList, [Args], RecListA), 307 compile_aux([ 308 (HeadA1 :- 309 (atomic(T); var(T)), 310 Apply 311 ), 312 (HeadA1 :- 313 compound(T), 314 T = [_|_], 315 !, 316 Apply, 317 RecList), 318 (HeadA1 :- 319 compound(T), 320 Apply, 321 T =.. [_|Args], 322 RecListA), 323 HeadL1, 324 (HeadL2 :- 325 HeadA1, 326 RecListA) 327 ], Module). 328 329 330% sumnodes(Pred, Term, AccIn, AccOut) 331% call Pred on all Subterms of Term and collect a result in Accumulator 332% traversal is depth-first and left-to-right 333 334t_sumnodes(sumnodes(Pred, Term, AccIn, AccOut), NewGoal, Module) :- 335 callable(Pred), 336 !, 337 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 338 aux_pred_name(sumnodes, Pattern, NewName), 339 append(VarArgs, [Term, AccIn, AccOut], NewGoalArgs), % make new goal 340 NewGoal =.. [NewName|NewGoalArgs], 341 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 342 append_args(HeadPrefix, [T, A0, A2], Head0), 343 append_args(GenPred, [T, A0, A1], Apply), 344 append_args(HeadPrefix, [T, A1, A2, 0, N], Call0), 345 append_args(HeadPrefix, [T, A1, A3, N0, Ar], Head1), 346 append_args(HeadPrefix, [Arg, A1, A2], Call1), 347 append_args(HeadPrefix, [T, A2, A3, N, Ar], Recursion), 348 compile_aux([ 349 (Head0 :- 350 Apply, 351 (compound(T) -> 352 functor(T, _, N), 353 Call0 354 ; 355 A1 = A2 356 )), 357 (Head1 :- 358 N0 < Ar -> 359 N is N0 + 1, 360 arg(N, T, Arg), 361 Call1, 362 Recursion 363 ; 364 A1 = A3) 365 ], Module). 366 367 368% sumargs(Pred, Term, AccIn, AccOut) 369% call Pred on all arguments of Term and collect a result in Accumulator 370% traversal order is unspecified! 371 372t_sumargs(sumargs(Pred, Term, AccIn, AccOut), NewGoal, Module) :- 373 callable(Pred), 374 !, 375 analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern), 376 aux_pred_name(sumargs, Pattern, NewName), 377 append(VarArgs, [Term, AccIn, AccOut], NewGoalArgs), % make new goal 378 NewGoal =.. [NewName|NewGoalArgs], 379 append_args(NewName, GenVarArgs, HeadPrefix), % make new predicate 380 append_args(HeadPrefix, [T, A0, A1], Head0), 381 append_args(HeadPrefix, [T, A0, A1, N], Call0), 382 append_args(HeadPrefix, [_, A0, A1, 0], NullHead), 383 append_args(HeadPrefix, [T, A1, A3, N], Head1), 384 append_args(GenPred, [Arg, A1, A2], Apply), 385 append_args(HeadPrefix, [T, A2, A3, N1], Recursion), 386 compile_aux([ 387 (Head0 :- 388 functor(T, _, N), 389 Call0), 390 (NullHead :- 391 !, A0 = A1), 392 (Head1 :- 393 arg(N, T, Arg), 394 N1 is N - 1, 395 Apply, 396 Recursion) 397 ], Module). 398 399 400 401% utilities 402 403compile_aux(Clauses, Module) :- 404 Clauses = [Clause|_], 405 ( Clause = (Head :- _) -> true 406 ; Clause = (delay Head if _) -> true 407 ; Clause = Head ), 408 functor(Head, F, N), 409 ( is_predicate(F/N)@Module -> 410% printf("*** Reusing auxiliary predicate %Qw\n%b", [F/N]), 411 true 412 ; 413 printf("*** Creating auxiliary predicate %Qw\n%b", [F/N]), 414% write_clauses(Clauses), 415 sepia_kernel:nested_compile_term(Clauses)@Module, 416 set_flag(F/N, auxiliary, on)@Module 417 ). 418 419write_clauses([]). 420write_clauses([C|Cs]) :- 421 writeclause(C), 422 write_clauses(Cs). 423 424 425append_args(Term, Args, NewTerm) :- 426 atom(Term), 427 NewTerm =.. [Term|Args]. 428append_args(Term, Args, NewTerm) :- 429 compound(Term), 430 Term =.. OldList, 431 append(OldList, Args, NewList), 432 NewTerm =.. NewList. 433 434 435:- mode analyse_pred(+,-,-,-,-). 436analyse_pred(Pred, VarArgs, GenPred, GenVarArgs, Pattern) :- 437 functor(Pred, F, N), 438 functor(Pattern, F, N), 439 functor(GenPred, F, N), 440 analyse_arg(Pred, VarArgs, GenPred, GenVarArgs, Pattern, 0, N). 441 442:- mode analyse_arg(+,-,+,-,+,+,+). 443analyse_arg(_Pred, [], _GenPred, [], _Pattern, Ar, Ar) :- !. 444analyse_arg(Pred, VarArgs, GenPred, GenVarArgs, Pattern, N0, Ar) :- 445 N is N0+1, 446 arg(N, Pred, Arg), 447 ( nonground(Arg) -> 448 arg(N, Pattern, '_'), 449 VarArgs = [Arg|VarArgs0], 450 GenVarArgs = [_Var|GenVarArgs0], 451 arg(N, GenPred, _Var) 452 ; 453 arg(N, Pattern, Arg), 454 arg(N, GenPred, Arg), 455 VarArgs = VarArgs0, 456 GenVarArgs = GenVarArgs0 457 ), 458 analyse_arg(Pred, VarArgs0, GenPred, GenVarArgs0, Pattern, N, Ar). 459 460 461aux_pred_name(Name, Pattern, NewName) :- 462 open("", string, S), 463 printf(S, "%a(%w)", [Name, Pattern]), 464 get_stream_info(S, name, NewNameS), 465 close(S), 466 atom_string(NewName, NewNameS). 467 468 469%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 470% 471% Definitions for Metacalls 472% 473%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 474 475:- local maplist_body/4. % hide the one from lib(lists) 476:- tool(maplist/3, maplist_body/4). 477 478maplist_body(_, [], [], _). 479maplist_body(Pred, [In|ListIn], [Out|ListOut], Module) :- 480 call(Pred, In, Out)@Module, 481 maplist_body(Pred, ListIn, ListOut, Module). 482 483:- tool(mapstream/3, mapstream_body/4). 484 485delay mapstream_body(_, L, _, _) if var(L). 486mapstream_body(_, [], [], _). 487mapstream_body(Pred, [In|ListIn], [Out|ListOut], Module) :- 488 call(Pred, In, Out)@Module, 489 mapstream_body(Pred, ListIn, ListOut, Module). 490 491:- tool(mapargs/3, mapargs_body/4). 492 493mapargs_body(Pred, TermIn, TermOut, Module) :- 494 functor(TermIn, F, N), 495 functor(TermOut, F, N), 496 mapargs_args(Pred, TermIn, TermOut, N, Module). 497 498mapargs_args(_, _, _, 0, _) :- !. 499mapargs_args(Pred, TermIn, TermOut, I, Module) :- 500 arg(I, TermIn, InArg), 501 arg(I, TermOut, OutArg), 502 I1 is I-1, 503 call(Pred, InArg, OutArg)@Module, 504 mapargs_args(Pred, TermIn, TermOut, I1, Module). 505 506:- tool(appnodes/2, appnodes_body/3). 507 508appnodes_body(Pred, Term, Module) :- 509 (atomic(Term); var(Term)), 510 call(Pred, Term)@Module. 511appnodes_body(Pred, Term, Module) :- 512 compound(Term), 513 Term = [_|_], 514 !, 515 call(Pred, Term)@Module, 516 appnodes_list(Pred, Term, Module). 517appnodes_body(Pred, Term, Module) :- 518 compound(Term), 519 call(Pred, Term)@Module, 520 Term =.. [_|Args], 521 appnodes_list(Pred, Args, Module). 522 523appnodes_list(_, [], _). 524appnodes_list(Pred, [Term|Args], Module) :- 525 appnodes_body(Pred, Term, Module), 526 appnodes_list(Pred, Args, Module). 527 528:- tool(sumargs/4, sumargs_body/5). 529 530sumargs_body(Pred, Term, A0, A1, Module) :- 531 functor(Term, _, N), 532 sumargs(Pred, Term, A0, A1, N, Module). 533 534sumargs(_, _, A0, A1, 0, _) :- 535 !, 536 A0 = A1. 537sumargs(Pred, Term, A1, A3, N, Module) :- 538 arg(N, Term, Arg), 539 N1 is N - 1, 540 call(Pred, Arg, A1, A2)@Module, 541 sumargs(Pred, Term, A2, A3, N1, Module). 542 543:- tool(applist/2, applist_body/3). 544:- tool(checklist/2, applist_body/3). 545 546applist_body(_, [], _). 547applist_body(Pred, [In|ListIn], Module) :- 548 append_args(Pred, [In], Goal), 549 call(Goal)@Module, 550 applist_body(Pred, ListIn, Module). 551 552:- tool(selectlist/3, selectlist_body/4). 553 554selectlist_body(_, [], [], _). 555selectlist_body(Pred, [In|ListIn], ListOut, Module) :- 556 (call(Pred, In)@Module -> 557 ListOut = [In|NewListOut] 558 ; 559 ListOut = NewListOut 560 ), 561 selectlist_body(Pred, ListIn, NewListOut, Module). 562 563:- tool(sumlist/4, sumlist_body/5). 564 565sumlist_body(_, [], Acc, Acc, _). 566sumlist_body(Pred, [H|T], AccIn, AccOut, Module) :- 567 call(Pred, H, AccIn, A1)@Module, 568 sumlist_body(Pred, T, A1, AccOut, Module). 569 570:- tool(sumnodes/4, sumnodes_body/5). 571 572sumnodes_body(Pred, Term, A0, A2, Module) :- 573 call(Pred, Term, A0, A1)@Module, 574 (compound(Term) -> 575 functor(Term, _, N), 576 sumnodes(Pred, Term, A1, A2, 0, N, Module) 577 ; % simple term or variable 578 A1 = A2 579 ). 580 581sumnodes(Pred, Term, A1, A3, N0, Ar, Module) :- 582 N0 < Ar -> 583 N is N0+1, 584 arg(N, Term, Arg), 585 sumnodes_body(Pred, Arg, A1, A2, Module), 586 sumnodes(Pred, Term, A2, A3, N, Ar, Module) 587 ; 588 A1 = A3. 589 590:- tool(fromto/4, fromto_body/5). 591 592fromto_body(From, To, Step, Pred, Module) :- 593 ( sgn(Step) =:= sgn(From-To) -> 594 true 595 ; 596 call(Pred, From)@Module, 597 From1 is From+Step, 598 fromto_body(From1, To, Step, Pred, Module) 599 ). 600 601