如何将程序算法转换为逻辑程序?

时间:2016-04-04 13:30:41

标签: algorithm prolog naming

如何从程序描述中实现Prolog中的算法,以便最好地利用逻辑范例。

我有时会发现很难从算法的给定程序描述中编写一个prolog程序。我举一个下面一个尝试的具体例子。注意我有谓谓innerforloop或convergedloop的谓词。这似乎不对,很难检查我的实现是否与算法描述相符。此代码是否与描述相符?这些谓词有什么更好的名字?或者代码应该以不同的方式组织?

示例算法:训练感知器分类器。

Input: labelled training data in D homogeneous coordinates;
       learning rate n.
Output: weight vector W defining classifier ŷ = sign(w⃗ ⋅  x⃗).

w⃗ <- 0;
converged <- false;
While converged = false do
  converged <- true;
  for i = 1 to |D| do
    if yiw⃗⋅x⃗i <0
    then
      w⃗<- w⃗+n * yi * x⃗i;
      converged <-false
    end
  end
end

我的实施:

perceptron(Pos,Neg,classifier(W,0)):-
   Pos =[OneExample|_],
   length(OneExample,NumberOfFeatures),
   populate_list(0,NumberOfFeatures,Zerovector),
   maplist(myappend([1]),Pos,PosClass),
   maplist(myappend([-1]),Neg,NegClass),
   append(PosClass,NegClass,Examples),
   convergedloop(Examples,Zerovector,W).

convergedloop(Examples,Inputvector,OutputVector):-
  innerforloop(Examples,Inputvector,UpdateVector),
  check_converged(Examples,Inputvector,UpdateVector,OutputVector).

check_converged(_Examples,W1,W2,W2):-W1=W2.
check_converged(Examples,W1,W2,W3):-
  dif(W1,W2),
  convergedloop(Examples,W2,W3).

innerforloop([],W,W).
innerforloop(ExamplesWithClass,W1,WFin):-
  ExamplesWithClass =[One|Rest],
  update_weight(One,W1,W2),
  innerforloop(Rest,W2,WFin).

learning_rate(0.1).

update_weight(Example_with_class,W1,W2):-
  misclassified(W1,Example_with_class,true),
  learning_rate(N),
  append(Example,[Class|[]],Example_with_class),
  Scalar is N *Class,
  scalar_multiplication(Scalar,Example,ExampleScaled),
  vector_addition(W1,ExampleScaled,W2).

update_weight(Example_with_class,W,W):-
  misclassified(W,Example_with_class,false).

misclassified(W,Example_with_class,TrueFalse):-
  append(Example,[Class|[]],Example_with_class),
  dot_product(Example,W,Dot),
  multiply(Dot,Class,Value),
  (   Value =<0->TrueFalse=true;TrueFalse=false).

%%%background predicates
myappend(Y,X,Z):-
  append(X,Y,Z).

multiply(X,Y,Z):-
  Z is X*Y.

vector_addition(X,Y,Z):-
  maplist(my_plus,X,Y,Z).

dot_product(V1,V2,Dot):-
  maplist(multiply,V1,V2,VMul),
  sumlist(VMul,Dot).

scalar_multiplication(S,X,SX):-
  maplist(multiply(S),X,SX).

populate_list(Pop,Length,ListPoped):-
  length(List,Length),findall(X,(member(X,List),X=Pop),ListPoped).

%%%Data
linear_sep_data(Pos,Negs,All):-
All=[
    [1.5998426,0.52985437,1],
            [0.25065517,1.30425162,1],
            [0.76148911,0.60419602,1],
            [0.75591032,-0.78994764,1],
            [1.63605539,0.9225655,1],
            [2.70520379,0.93285704,1],
            [1.82870703,2.34804646,1],
            [-0.08549264,0.99868399,1],
            [0.44906531,0.90555838,1],
            [0.49966187,1.59299327,1],
            [1.00003726,-0.13822094,1],
            [1.67943676,1.25283262,1],
            [-1.00158649,2.73839505,1],
            [3.32539035,-0.39289509,1],
            [2.17885898,0.05984356,1],
            [1.85977529,0.76782626,1],
            [1.34470454,0.18312675,1],
            [0.5974872,0.1228956,1],
            [-1.52394333,-1.24558361,-1],
            [-2.48452861,-1.91070328,-1],
            [-1.04605257,-2.55270759,-1],
            [1.02370408,-1.67944911,-1],
            [-0.80492117,-1.49215482,-1],
            [-1.64954319,-3.41635041,-1],
            [-2.35543276,-0.37750433,-1],
            [-0.32384031,-2.08235145,-1],
            [-1.56576954,-1.22018985,-1],
            [-1.27853841,-1.28469686,-1],
            [-1.97696119,0.23717806,-1],
            [-1.78965834,-1.09026084,-1]],
findall(Instance,(member(InstanceC,All),append(Instance,[1|[]],InstanceC)),Pos),
findall(Instance,(member(InstanceC,All),append(Instance,[-1|[]],InstanceC)),Negs).

1 个答案:

答案 0 :(得分:2)

在Prolog中命名事物的关键见解是逻辑程序在实体之间定义关系。理想情况下,谓词名称可以指示每个参数描述哪些实体。

因此,在这种情况下,forloopinnerloop等名称正确地感觉错误:来自forloop之类的名称(或更具可读性:{ {1}}),我们宁愿期望例如语法描述&#34; for循环&#34;是,但肯定不是循环应该执行的实际计算

命令算法描述了一系列全局状态的破坏性修改。为了将命令式算法转换为声明性范式,我们改变了我们的观点,而是描述了状态之间的关系

我们使用以下命名约定,这在许多程序中都很有用:for_loop表示初始状态。 S0表示计算的一个步骤之后的状态。 S1S2等表示其他状态,最后,S3表示最终状态

现在考虑你引用的具体例子:

命令式术语中算法的本质是重复修改状态,直到达到最终状态。在声明范例中描述这种状态转换序列的一种方法如下:

state0_state(S0, S) :-
     state_next(S0, S1),
     state0_state_(S0, S1, S).

state0_state_(S0, S, S) :-
     close_enough(S0, S).
state0_state_(S0, S1, S) :-
     not_close_enough(S0, S1),
     state0_state(S1, S).

您必须提供Sstate_next/2close_enough/2。我将此作为练习。请注意,谓词not_close_enough/2的命名方式是清楚每个参数表示的内容。经过深思熟虑后,您可能会找到更具描述性的名称,例如state0_state/2

根据您执行此操作的程度,我们观察到声明性范例的一个有价值的优势:代码可以在所有方向 使用。这意味着您也可以发布最常见的查询:

training0_training/2

并且理想地获得这种关系中的状态。

命名谓词的另一个提示:避免命令。在声明范式中,命令毫无意义,因为许多谓词可以用于多个方向,而命令总是意味着单一方向。