Switch statements in Prolog
Asked Answered
P

3

10

In Prolog predicates, I often write repetitive conditional statements like this one, but I wish they could be written more concisely:

output(Lang, Type, Output) :-   
    (Lang = javascript ->
        Output = ["function", Type];
    Lang = ruby ->
        Output = ["def", Type];
    Lang = java ->
        Output = [Type]).

Would it be possible to replace this series of conditional statements with a more concise switch-statement?

Pye answered 3/3, 2016 at 6:32 Comment(2)
The parentheses you have written around the conditions are redundant. You should instead place a single pair of parentheses around the whole (a->b;c->d;e) construct. This is best practice to avoid surprises when you want to conjoin the conditional with other goals.Plafond
@Plafond I fixed the problem, so it seems more concise now.Pye
P
10

In Prolog it is quite easy to define your own control structures, using meta-predicates (predicates that take goals or predicates as arguments).

For example, you could implement a switch construct like

switch(X, [
    a : writeln(case1),
    b : writeln(case2),
    c : writeln(case3)
])

by defining

switch(X, [Val:Goal|Cases]) :-
    ( X=Val ->
        call(Goal)
    ;
        switch(X, Cases)
    ).

If necessary, this can then be made more efficient by compile-time transformation as supported by many Prolog systems (inline/2 in ECLiPSe, or goal expansion in several other systems).

And via operator declarations you can tweak the syntax to pretty much anything you like.

Plafond answered 4/3, 2016 at 15:59 Comment(0)
C
7

It seems that multiple clauses are made for this use case and also quite concise.

output(javascript, Type, ["javascript", Type]).
output(ruby, Type, ["def", Type]).
output(java, Type, [Type]).
Camiecamila answered 3/3, 2016 at 7:15 Comment(2)
Of course, it will only be concise if the predicate has a small number of arguments. I'm still searching for a more concise way to implement switch statements in Prolog.Pye
For sure, multiple clauses are not made for this use case. They are the main control flow tool available in Prolog. As such, of course they cover this banal use case.Pilfer
P
1

slightly shorter:

output(Lang, Type, Output) :-   
    (Lang, Output) = (javascript, ["function", Type]) ;
    (Lang, Output) = (ruby, ["def", Type]) ;
    (Lang, Output) = (java, [Type]).

idiomatic:

output(Lang, Type, Output) :-
  memberchk(Lang-Output, [
    javascript - ["function", Type],
    ruby - ["def", Type],
    java - [Type]
  ]).
Pilfer answered 3/3, 2016 at 6:49 Comment(2)
Using memberchk/2 is a rather inefficient way on structure copying Prolog implementations (which are AFAIK all currently actively maintained implementations) as it first copies the entire structure to the stack, then runs memberchk/2 and finally leaves recollection of the copied structure to GC. The first is more acceptable, but using separate clauses as the next answe is the real Prolog way: consise and fast due to clause indexing.Barretter
@JanWielemaker: I agree, my answer is not good... Also, my first snippet is not equivalent to OP' code. but neither separate clauses would be, without cuts...Pilfer

© 2022 - 2024 — McMap. All rights reserved.