Pure Prolog Scheme Quine

前端 未结 3 646
走了就别回头了
走了就别回头了 2021-01-21 06:15

There is this paper:

William E. Byrd, Eric Holk, Daniel P. Friedman, 2012
miniKanren, Live and Untagged
Quine Generation via Relational Interpret

3条回答
  •  别那么骄傲
    2021-01-21 06:34

    I'm using SWI Prolog with the occurs check turned on here (but dif/2 skips the occurs check anyway):

    symbol(X) :- freeze(X, atom(X)).
    
    symbols(X) :- symbol(X).
    
    symbols([]).
    
    symbols([H|T]) :-
        symbols(H),
        symbols(T).
    
    % lookup(X, Env, Val).
    %
    % [quote-unbound(quote)] will be the empty environment
    % when unbound(quote) is returned, this means that
    % `quote` is unbound
    
    lookup(X, [X-Val|_], Val).
    
    lookup(X, [Y-_|Tail], Val) :- 
        dif(X, Y),
        lookup(X, Tail, Val).
    
    % to avoid name clashing with `eval`
    %
    % evil(Expr, Env, Val).
    
    evil([quote, X], Env, X) :-
        lookup(quote, Env, unbound(quote)),
        symbols(X).
    
    evil(Expr, Env, Val) :-
        symbol(Expr),
        lookup(Expr, Env, Val),
        dif(Val, unbound(quote)).
    
    evil([lambda, [X], Body], Env, closure(X, Body, Env)).
    
    evil([list|Tail], Env, Val) :-
        evil_list(Tail, Env, Val).
    
    evil([E1, E2], Env, Val) :- 
        evil(E1, Env, closure(X, Body, Env1_Old)),
        evil(E2, Env, Arg), 
        evil(Body, [X-Arg|Env1_Old], Val).
    
    evil([cons, E1, E2], Env, Val) :-
        evil(E1, Env, E1E),
        evil(E2, Env, E2E),
        Val = [E1E | E2E].
    
    evil_list([], _, []).
    evil_list([H|T], Env, [H2|T2]) :-
        evil(H, Env, H2), evil_list(T, Env, T2).
    
    % evaluate in the empty environment
    
    evil(Expr, Val) :-
        evil(Expr, [quote-unbound(quote)], Val).
    

    Tests:

    Find Scheme expressions that eval to (i love you) -- this example has a history in miniKanren:

    ?- evil(X, [i, love, you]), print(X).
    [quote,[i,love,you]]
    X = [quote, [i, love, you]] ;
    [list,[quote,i],[quote,love],[quote,you]]
    X = [list, [quote, i], [quote, love], [quote, you]] ;
    [list,[quote,i],[quote,love],[[lambda,[_3302],[quote,you]],[quote,_3198]]]
    X = [list, [quote, i], [quote, love], [[lambda, [_3722], [quote|...]], [quote, _3758]]],
    dif(_3722, quote),
    freeze(_3758, atom(_3758)) ;
    [list,[quote,i],[quote,love],[[lambda,[_3234],_3234],[quote,you]]]
    X = [list, [quote, i], [quote, love], [[lambda, [_3572], _3572], [quote, you]]],
    freeze(_3572, atom(_3572)) ;
    

    In other words, the first 4 things it finds are:

    (quote (i love you))
    
    (list (quote i) (quote love) (quote you))
    
    (list (quote i) (quote love) ((lambda (_A) (quote you)) (quote _B)))
    ; as long as _A != quote
    
    (list (quote i) (quote love) ((lambda (_A) _A) (quote you))) 
    ; as long as _A is a symbol
    

    It looks like the Scheme semantics are correct. The language-lawyer type of constraints it places are pretty neat. Indeed, real Scheme will refuse

    > (list (quote i) (quote love) ((lambda (quote) (quote you)) (quote _B)))
    Exception: variable you is not bound
    Type (debug) to enter the debugger.
    

    but will accept

    > (list (quote i) (quote love) ((lambda (quote) quote) (quote you)))
    (i love you)
    

    So how about quines?

    ?- evil(X, X).
    
    

    miniKanren uses BFS, so maybe that's why it produces results here. With DFS, this could work (assuming there are no bugs):

    ?- call_with_depth_limit(evil(X, X), n, R).
    

    or

    ?- call_with_inference_limit(evil(X, X), m, R).
    

    but SWI doesn't necessarily limit the recursion with call_with_depth_limit.

提交回复
热议问题