predicado “binario a número” reversible

¿Cuál es la mejor manera de convertir bits binarios (puede ser una lista de 0/1, por ejemplo) en números de una manera reversible. He escrito un predicado nativo en swi, pero ¿hay una mejor solución? Atentamente

Use restricciones CLP (FD), por ejemplo:

 :- use_module(library(clpfd)). binary_number(Bs0, N) :- reverse(Bs0, Bs), foldl(binary_number_, Bs, 0-0, _-N). binary_number_(B, I0-N0, IN) :- B in 0..1, N #= N0 + B*2^I0, I #= I0 + 1. 

Consultas de ejemplo:

 ?- binary_number([1,0,1], N). N = 5. ?- binary_number(Bs, 5). Bs = [1, 0, 1] . ?- binary_number(Bs, N). Bs = [], N = 0 ; Bs = [N], N in 0..1 ; etc. 

Aquí está la solución en la que estaba pensando, o mejor dicho, lo que esperaba que existiera.

 :- use_module(library(clpfd)). binary_number(Bs, N) :- binary_number_min(Bs, 0,N, N). binary_number_min([], N,N, _M). binary_number_min([B|Bs], N0,N, M) :- B in 0..1, N1 #= B+2*N0, M #>= N1, binary_number_min(Bs, N1,N, M). 

Esta solución también finaliza para consultas como:

 ?- Bs = [1|_], N #=< 5, binary_number(Bs, N). 

La solución

Esta respuesta busca proporcionar un predicado binary_number/2 que presente tanto la pureza lógica como las mejores propiedades de terminación. He usado when/2 para detener consultas como canonical_binary_number(B, 10) de entrar en infinito bucle después de encontrar la primera solución (única). Hay una compensación, por supuesto, el progtwig tiene metas redundantes ahora.

 canonical_binary_number([0], 0). canonical_binary_number([1], 1). canonical_binary_number([1|Bits], Number):- when(ground(Number), (Number > 1, Pow is floor(log(Number) / log(2)), Number1 is Number - 2 ^ Pow, ( Number1 > 1 -> Pow1 is floor(log(Number1) / log(2)) + 1 ; Pow1 = 1 ))), length(Bits, Pow), between(1, Pow, Pow1), length(Bits1, Pow1), append(Zeros, Bits1, Bits), maplist(=(0), Zeros), canonical_binary_number(Bits1, Number1), Number is Number1 + 2 ^ Pow. binary_number(Bits, Number):- canonical_binary_number(Bits, Number). binary_number([0|Bits], Number):- binary_number(Bits, Number). 

Pureza y terminación

Yo afirmo que este predicado presenta pureza lógica desde la construcción. Espero haberlo hecho bien de estas respuestas: uno , dos y tres .

Cualquier objective con argumentos adecuados termina. Si es necesario verificar los argumentos, la forma más sencilla de lograr esto es usar la length/2 incorporada:

 binary_number(Bits, Number):- length(_, Number), canonical_binary_number(Bits, Number). ?- binary_number(Bits, 2+3). ERROR: length/2: Type error: `integer' expected, found `2+3' Exception: (6) binary_number(_G1642009, 2+3) ? abort % Execution Aborted ?- binary_number(Bits, -1). ERROR: length/2: Domain error: `not_less_than_zero' expected, found `-1' Exception: (6) binary_number(_G1642996, -1) ? creep 

Consultas de ejemplo

 ?- binary_number([1,0,1|Tail], N). Tail = [], N = 5 ; Tail = [0], N = 10 ; Tail = [1], N = 11 ; Tail = [0, 0], N = 20 . ?- binary_number(Bits, 20). Bits = [1, 0, 1, 0, 0] ; Bits = [0, 1, 0, 1, 0, 0] ; Bits = [0, 0, 1, 0, 1, 0, 0] ; Bits = [0, 0, 0, 1, 0, 1, 0, 0] ; Bits = [0, 0, 0, 0, 1, 0, 1, 0, 0] . ?- binary_number(Bits, N). Bits = [0], N = 0 ; Bits = [1], N = 1 ; Bits = [1, 0], N = 2 ; Bits = [1, 1], N = 3 ; Bits = [1, 0, 0], N = 4 ; Bits = [1, 0, 1], N = 5 . 

jugando con bits …

 binary_number(Bs, N) :- var(N) -> foldl(shift, Bs, 0, N) ; bitgen(N, Rs), reverse(Rs, Bs). shift(B, C, R) :- R is (C < < 1) + B. bitgen(N, [B|Bs]) :- B is N /\ 1 , ( N > 1 -> M is N >> 1, bitgen(M, Bs) ; Bs = [] ).