Evaluate a number in (a few) natural language

119 Views Asked by At

Assume a list, each element can be:

a) a number 1,2,...9

b) a number 10, 100, 10000, ... (numbers of the form 10^(2^n) with n>=0).

It is need a (as much simple as possible) rule that evaluates this list to one integer number. Examples of this evaluation are:

[1] => 1
[2] => 2
[10 1] => 11
[2 10 1] => 21
[2 100 1 10 4] => 214
[2 10 1 100 4] => 2104
[2 10 1 100 10000] => 21000000

In other words, numbers 10, 100, ... are the equivalent of tenths, hundreds, million, ... in english and the rule to evaluate is the usual in english and other languages: 10, 100 "multiplies" the values before them, numbers after them are added.

(I know this definition is not an exact one, but finding a good definition is part of the problem. Do not hesitate to requests for more examples if necessary).

Note than, in the same way than in natural language, the number zero is not necessary. Even, like initial languages, is not present in the grammar.

Addendum

The major difficulty in this problem is an expression like [2 10000 3 10] that can not be taken as (2*10000+3)*10, but as 2*10000+3*10. Another example is [2 10 1 10000 3 10] that is (2*10+1)*10000+3*10.

Proof of not homework: Interest on this numbering (and, in general, in natural language) is that, in some context, they are more error-safe than binary. By example, in a context of a supermarket prices, "two thousands blah" keeps some meaning, while 1001blah is totally undefined.

1

There are 1 best solutions below

4
On

With ingenuity, I would start covering the patterns...

test :- forall(member(L=R, [
    [1] = 1,
    [2] = 2,
    [10, 1] = 11,
    [2, 10, 1] = 21,
    [2, 100, 1, 10, 4] = 214,
    [2, 10, 1, 100, 4] = 2104,
    [2, 10, 1, 100, 10000] = 21000000
    ]), test(L, R)).

test(L, R) :-
    pattern(L, E), R =:= E -> writeln(ok(L,R)) ; writeln(ko(L,R)).

pattern([A], A) :- dig(A).
pattern([A, B], A+B) :- ten(A), dig(B).
pattern([A, B, C], A*B+C) :- mul_ten(A, B), dig(C).

pattern([A, B, C, D, E], A*B + C*D + E) :- mul_ten(A,B), mul_ten(C,D), B > D, dig(E).
pattern([A, B, C, D, E], ((A*B+C)*D)+E) :- mul_ten(A,B), ten(D), dig(E). % doubt...
pattern([A, B, C, D, E], (A*B+C)*D*E) :- mul_ten(A,B), ten(D), ten(E). % doubt...


dig(D) :- between(1,9,D).
ten(T) :- between(0,10,E), T =:= 10^(2^E). % 10 -> inappropriate (too much zeroes ?)
mul_ten(M,T) :- between(1,9,M), ten(T).    % 9 -> inappropriate ?

plain pattern matching. Running:

?- test.
ok([1],1)
ok([2],2)
ok([10,1],11)
ok([2,10,1],21)
ok([2,100,1,10,4],214)
ok([2,10,1,100,4],2104)
ok([2,10,1,100,10000],21000000)
true.

I think that there is little space for recursion, afaik idioms cover frequently used cases, but without 'smart' evaluation... Anyway, I cannot really find my way in (that is, I would never use) this pattern

[2 10 1 100 4] => 2104

edit now, with DCG and CLP(FD) :

:- use_module(library(clpfd)).

test :- forall(member(L=R, [
    [1] = 1,
    [2] = 2,
    [10, 1] = 11,
    [2, 10, 1] = 21,
    [2, 100, 1, 10, 4] = 214,
    [2, 10, 1, 100, 4] = 2104,
    [2, 10, 1, 100, 10000] = 21000000
    ]), test(L, R)).

test(L, R) :-
    phrase(pattern(E), L), R #= E -> writeln(ok(L,R)) ; writeln(ko(L,R)).

pattern(A) --> dig(A).
pattern(A+B) --> ten(A), dig(B).
pattern(A*B+C) --> mul_ten(A, B), dig(C).
pattern(A*B+C*D) --> mul_ten(A, B), mul_ten(C, D).
pattern(A*B + C*D + E) --> mul_ten(A,B), mul_ten(C,D), dig(E).
pattern(((A*B+C)*D)+E) --> mul_ten(A,B), [C], ten(D), dig(E). % doubt...
pattern((A*B+C)*D*E) --> mul_ten(A,B), [C], ten(D), ten(E). % doubt...

dig(D) --> [D], {D #>= 1, D #=< 9}.
ten(T) --> [T], {T #>= 1, T #= (10^(2^E)), E #> 0, E #=< 10}.
mul_ten(M,T) --> dig(M), ten(T).

edit I like the op/3 directive, also...

:- op(100,fx, dig).
:- op(100,fx, ten).
:- op(100,xfx, mul).

pattern(A) --> dig A.
pattern(A+B) --> ten A, dig B.
pattern(A*B+C) --> A mul B, dig(C).
pattern(A*B+C*D) --> A mul B, C mul D.
pattern(A*B+C*D+E) --> A mul B, C mul D, dig E.
pattern(((A*B+C)*D)+E) --> A mul B, [C], ten D, dig E. % doubt...
pattern((A*B+C)*D*E) --> A mul B, [C], ten D, ten E. % doubt...

dig D --> [D], {D #>= 1, D #=< 9}.
ten T --> [T], {T #>= 1, T #= (10^(2^E)), E #> 0, E #=< 10}.
M mul T --> dig M, ten T.