Prolog 使用动态事实来模拟;堆栈";在序言中

Prolog 使用动态事实来模拟;堆栈";在序言中,prolog,Prolog,所以我基本上是在用Prolog模拟asm代码 在@mbrach的帮助下,我知道使用动态事实模拟指令(如 add eax, 1 mov eax, 1 这样: :- dynamic(register/2). % Fill in as needed register(eax, 0). .... add(Reg, Value) :- ( retract(register(Reg, OldValue)) -> NewValue is OldValue + Value ), asser

所以我基本上是在用Prolog模拟asm代码

在@mbrach的帮助下,我知道使用动态事实模拟指令(如

add eax, 1
mov eax, 1
这样:

:- dynamic(register/2).  % Fill in as needed

register(eax, 0).
....
add(Reg, Value) :-
(   retract(register(Reg, OldValue))
 ->   NewValue is OldValue + Value
),
assertz(register(Reg, NewValue)).
但问题是,如何以类似的方式模拟堆栈

最初,我编写了一些相当FP风格的代码,如下所示:

nth_member(1, [M|_], M).
nth_member(N, [_|T], M) :- N>1, N1 is N - 1, nth_member(N1, T, M).
.....
% push  ebp
ESP is ESP - 1, nth_member(ESP, STACK, EBP),
....
但问题是我不知道如何用动态事实风格重写这段代码


谁能给我一些帮助。。?谢谢大家!

序言中说,除非你有很好的理由,否则不要使用动态事实作为状态。换句话说,如果要对堆栈进行建模,请将其作为一个术语进行维护,并将其作为状态参数传递给递归谓词的下一步。例如(非常简化)

第二个参数,
Final\u stack
,如果您希望在完成所模拟代码中的所有指令后获得最终堆栈,那么它就存在了。在模拟开始时,它可能是一个自由变量,或者,如果您想验证预期的最终状态,它可能是一个自由变量

堆栈本身可能是一个列表(如果您只需要堆栈的顶部),也可能是一个更复杂的嵌套术语。您可能也希望以这种方式维护所有寄存器(如中所示)

还有另一种选择,使用适当的可变全局变量。根据您使用的Prolog实现,这将涉及不同的内置程序。对于SWI Prolog,请看;对于GNU Prolog。其他实现可能也会有相同的谓词

这里的要点是,使用assert和retract来维护频繁更改的状态会使您的程序很难理解,而且效率很低。“纯”Prolog解决方案是第一个建议;在某些情况下,使用全局变量可能更有效

附言:

作为如何使用堆栈的完整示例,请参阅以下关于基于堆栈的计算器(无耻的自我提升)问题的答案:

为了扩展“不要使用动态谓词”,它们肯定有自己的用途。当这是一个好的解决方案时,一个很好的例子是如果您正在实现一个关系数据库。然后,表被实现为事实,每列一个子句:

name_age('Bob', 20).
name_age('Jane', 23).
% etc

name_occupation('Bob', student).
name_occupation('Jane', teacher).
% etc

在这里,您可以使用断言向表中添加新行,或使用收回删除行。主要的一点是,查询数据库的频率可能比修改数据库的频率要高得多。您还可以从Prolog对事实的高效查找中获益,此外,您还可以以更自然的方式编写查询。

我想强调@Boris提出的一点:不要使用动态谓词

到目前为止,最干净的解决方案是使用状态变量来携带模拟机器的当前状态。由于Prolog的单一赋值特性,您将始终拥有这两对:状态前和状态后。对于寄存器和内存,状态最好表示为将寄存器名称(或内存地址)映射到值的表。堆栈可以简单地保存为列表。例如:

main :-
    Stack0 = [],
    Regs0 = [eax-0, ebx-0, ecx-0, edx-0],
    Code = [movi(3,eax), add(eax,7), push(eax), pop(ecx)],
    sim_code(Code, Regs0, RegsN, Stack0, StackN),
    write(RegsN), nl, write(StackN), nl.

% simulate a sequence of instructions
sim_code([], Regs, Regs, Stack, Stack).
sim_code([Instr|Instrs], Regs0, RegsN, Stack0, StackN) :-
    sim_instr(Instr, Regs0, Regs1, Stack0, Stack1),
    sim_code(Instrs, Regs1, RegsN, Stack1, StackN).

% simulate one instruction
sim_instr(movi(Value,Reg), Regs0, RegsN, Stack, Stack) :-
    update(Regs0, Reg, _Old, Value, RegsN).
sim_instr(add(Reg,Value), Regs0, RegsN, Stack, Stack) :-
    update(Regs0, Reg, Old, New, RegsN),
    New is Old+Value.
sim_instr(push(Reg), Regs, Regs, Stack, [Val|Stack]) :-
    lookup(Regs, Reg, Val).
sim_instr(pop(Reg), Regs0, RegsN, [Val|Stack], Stack) :-
    update(Regs0, Reg, _Old, Val, RegsN).
%sim_instr(etc, ...).

% simple key-value table (replace with more efficient library predicates)
lookup([K-V|KVs], Key, Val) :-
    ( Key==K -> Val=V ; lookup(KVs, Key, Val) ).

update([K-V|KVs], Key, Old, New, KVs1) :-
    ( Key==K ->
        Old = V, KVs1 = [K-New|KVs]
    ;
        KVs1 = [K-V|KVs2],
        update(KVs, Key, Old, New, KVs2)
    ).

实际上,您应该用高效的哈希或基于树的版本替换我的简单表实现(lookup/3、update/5)。这些不是标准化的,但您通常可以在Prolog系统附带的库中找到一个。

您最终是否还需要内存等?了解模拟的总体需求可以影响哪种方法最适合堆栈等@mbracch en.。是的,我想我需要它。所以基本上我试图模拟的是gnu coreutils中的简单asm程序翻译(gcc-c)…汇编程序和模拟器是完全不同的东西。汇编程序将汇编指令翻译成机器代码,而模拟器则通过指令的执行(可以模拟机器代码或直接汇编)进行操作。@mbratch e.。是的,我知道。。。那么你能告诉我你的观点吗…?对不起,我不清楚-我的观点是我想确保我了解你是想模拟还是组装混乱是我的错。如果您使用内存进行完整的模拟,那么堆栈模拟实际上可能是内存模拟和asm指令模拟的组合;还可以查看
库(记录)
(至少在SWI Prolog中可用,但这不是一个新想法,因此它可能以某种形式在其他地方可用)以使用带命名参数的术语,或者,对于SWI Prolog 7,Dicts。
main :-
    Stack0 = [],
    Regs0 = [eax-0, ebx-0, ecx-0, edx-0],
    Code = [movi(3,eax), add(eax,7), push(eax), pop(ecx)],
    sim_code(Code, Regs0, RegsN, Stack0, StackN),
    write(RegsN), nl, write(StackN), nl.

% simulate a sequence of instructions
sim_code([], Regs, Regs, Stack, Stack).
sim_code([Instr|Instrs], Regs0, RegsN, Stack0, StackN) :-
    sim_instr(Instr, Regs0, Regs1, Stack0, Stack1),
    sim_code(Instrs, Regs1, RegsN, Stack1, StackN).

% simulate one instruction
sim_instr(movi(Value,Reg), Regs0, RegsN, Stack, Stack) :-
    update(Regs0, Reg, _Old, Value, RegsN).
sim_instr(add(Reg,Value), Regs0, RegsN, Stack, Stack) :-
    update(Regs0, Reg, Old, New, RegsN),
    New is Old+Value.
sim_instr(push(Reg), Regs, Regs, Stack, [Val|Stack]) :-
    lookup(Regs, Reg, Val).
sim_instr(pop(Reg), Regs0, RegsN, [Val|Stack], Stack) :-
    update(Regs0, Reg, _Old, Val, RegsN).
%sim_instr(etc, ...).

% simple key-value table (replace with more efficient library predicates)
lookup([K-V|KVs], Key, Val) :-
    ( Key==K -> Val=V ; lookup(KVs, Key, Val) ).

update([K-V|KVs], Key, Old, New, KVs1) :-
    ( Key==K ->
        Old = V, KVs1 = [K-New|KVs]
    ;
        KVs1 = [K-V|KVs2],
        update(KVs, Key, Old, New, KVs2)
    ).