CodeIQ:4つの数と四則と括弧の解答

@nabetniさんのCodeIQの問題です。問題と解説はこちら
->http://nabetani.hatenablog.com/entry/codeiq_4dob_q820

括弧は計算の順序を規定するに過ぎませんので、
すべての順序の計算を網羅すれば括弧はいらないはずです。

途中で割り算を行うと最終結果が整数でも計算誤差による少数が出る可能性がありますので、
最初はswi-prologに有理数がない前提で、
sel/3の一行目で計算結果が整数からの誤差が小さいもの（目の子で）のみ整数とみなしました。
sel/3においてmember/2で演算記号と二数のすべての組み合わせで選び、
Fn=..\[F1,X1,X2](univ)で四則演算を作り、is(R1,Fn)で答えを求めます。
計算で使った二数をリストから除き、計算結果の数字をリストに加え、
残った数字が一つになるまで計算を繰り返します。
sel1/3は計算途中の数をそのまま使う場合と合成して二桁以上の数としてつかう場合です。
計算で出る数をfindallですべて集めて、
それをsubtract/3で1から100までのリストからのぞいています。

```Prolog
%SWI-Prolog (7.4.2,7.6.4)
%:-initialization(start).    %ideone
%start.

sel([R1],_,R):- R is round(R1),abs(R-R1)<0.01.   
sel([R],_,R).
sel(L,F,FR):-
     select(X1,L,L1),select(X2,L1,L2),member(F1,F),
     Fn=..[F1,X1,X2],is(R1,Fn),L3=[R1|L2],sel(L3,F,FR).     
 
sel1(L,F,FR):-sel(L,F,FR).
sel1(L,F,FR):-select(X1,L,L1),select(X2,L1,L2),
     X2<10,R1 is X1*10+X2,L3=[R1|L2],sel1(L3,F,FR).

start:-L=[2,3,7,8],F=[+,-,*,/],findall(X,(sel1(L,F,X),X>0,X=<100),R4),
          numlist(1,100,NL),subtract(NL,R4,R),write(R).          

% 54, 59, 71, 76, 80, 85, 94, 95
```
別法
除法による計算誤差を防ぐため、calc/7の4行目のmul/3で、割る代わりに他の数に除数をかけ、
除数（の積）を記録しておき、数字が一つになったら除数の積で割ります。
乗法の時は除数の積の二乗が掛かることになりますので、かけた後除数の積で割ります。

```Prolog
%SWI-Prolog (7.4.2,7.6.4)
%:-initialization(start).    %ideone
%start.

mul([],_,[]).
mul([H|T],Y,R):-mul(T,Y,R1),Z is Y*H,R=[Z|R1].

calc(X,'+',Y,L,D,RL,D):-R is X+Y,RL=[R|L].
calc(X,'-',Y,L,D,RL,D):-R is X-Y,RL=[R|L].
calc(X,'*',Y,L,D,RL,D):-R is X*Y/D,RL=[R|L].
calc(X,'/',Y,L,D,RL,RD):-RD is D*Y,mul(L,Y,R1),RL=[X|R1].  

sel([X],_,Y,R):-R is X /Y.
sel(L,F,D,FR):-
     select(X1,L,L1),select(X2,L1,L2),
     member(F1,F),
     calc(X1,F1,X2,L2,D,RL,RD),sel(RL,F,RD,FR).  
 
sel1(L,F,FR):-sel(L,F,1,FR).
sel1(L,F,FR):-select(X1,L,L1),select(X2,L1,L2),
     X2<10,R1 is X1*10+X2,L3=[R1|L2],sel1(L3,F,FR).

start:-L=[2,3,7,8],F=[+,-,*,/],findall(X,(sel1(L,F,X),X>0,X=<100),R4),
          numlist(1,100,NL),subtract(NL,R4,R),write(R).

```
調べたらswi-prologには有理数がありました。有理数があると楽です。
有理数形式の割り算が 「/」とは違いますので、他の演算も含めcalc/4で定義しなおします。
calcの4番目のrdivが有理数形式の割り算です。

```Prolog
%SWI-Prolog (7.4.2,7.6.4)
%:-initialization(start).    %ideone
%start.

calc(X,'+',Y,R):-R is X+Y.
calc(X,'-',Y,R):-R is X-Y.
calc(X,'*',Y,R):-R is X*Y.
calc(X,'/',Y,R):-R is X rdiv Y. 

sel([R],_,R).
sel(L,F,FR):-
     select(X1,L,L1),select(X2,L1,L2),member(F1,F),
     calc(X1,F1,X2,R1),L3=[R1|L2],sel(L3,F,FR). 
sel1(L,F,FR):-sel(L,F,FR).

sel1(L,F,FR):-select(X1,L,L1),select(X2,L1,L2),
     X2<10,R1 is X1*10+X2,L3=[R1|L2],sel1(L3,F,FR).

solv6(R):-L=[2,3,7,8],F=[+,-,*,/],findall(X,(sel1(L,F,X),X>0,X=<100),R4),
          numlist(1,100,NL),subtract(NL,R4,R).

start:-L=[2,3,7,8],F=[+,-,*,/],findall(X,(sel1(L,F,X),X>0,X=<100),R4),
          numlist(1,100,NL),subtract(NL,R4,R),write(R).          
```
2022.9.3追加
投稿後出題者の解説を見ましたら
　ゼロ除算へのケアがない方や、（中略）のに偶然正解している方もいらっしゃった
とのことでした。
私の解答もゼロ除算へのケアがありませんでした。
`zero_divisor'エラーはfindallでも吸収されません。
'別法'のコードで訂正例をお示しします。

```Prolog
%calc(X,'/',Y,L,D,RL,RD):-RD is D*Y,mul(L,Y,R1),RL=[X|R1].
calc(X,'/',Y,L,D,RL,RD):-Y =\=0,RD is D*Y,mul(L,Y,R1),RL=[X|R1].  
```
2022.9.3さらに追加
正解値はこちらにお世話になりました。解答もあります。
->https://gist.github.com/cielavenir/11185327