水星でのアインシュタインの仕事

アインシュタインのHabréでの仕事の週を続けます。 提示された3つのソリューションに加えて

  1. 通常の言語
  2. ハスケル
  3. プロローグ


水星でもう一つ紹介しましょう。



ウィキペディアを思い出してください



Mercuryは強力な型付け機能を備えた機能的な型付け言語です...



すぐにコメントできたらいいなと思います。 水銀は、より安全で高速なプロローグを意味するタイピング可能なものとして考えられていたという事実にもかかわらず、私は論理にこだわっているように思えます。 説明します。 プロローグでは、ほとんどの最も重要なツールはいわゆる論理変数です 。 ここでは、おそらく、例(プロローグ)を使用して説明する方が簡単です。



 ?- A=B, C=B, B=D, D=123. A = 123, B = 123, C = 123, D = 123.
      
      





ここでは、変数間の関係を設定し、最後にのみ具体化します。 この場合、残りの変数は関係に従って自動的に具体化されます。



 ?- L=[A,B,C], L=[5,5|_], C=Q, Q=B. L = [5, 5, 5], A = 5, B = 5, C = 5, Q = 5.
      
      





より複雑な例。 これまでに不明な3つの変数A、B、CのリストLが指定され、リストの最初の2つの要素が5で指定され、同時に変数AとBが同じ番号で自動的に指定されました。 次に、C = Q、a Q = B、および判明したようにB = 5であるため、Cは同じ番号で指定されました。



 ?- length(L,3). L = [_G328, _G331, _G334].
      
      





3つの未知の値(非特定)のリストを作成しました。



 ?- length(L,3), nth0(0,L,aaa). L = [aaa, _G461, _G464].
      
      





リストの最初の要素がアトムaaa



と等しいことを明確にしました。 さらに、リスト自体は部分的に指定されていました。



 ?- length(L,3), nth0(0,L,aaa), nth0(1,L,bbb). L = [aaa, bbb, _G594].
      
      





リストをさらに改良しました。



 ?- length(L,3), nth0(0,L,aaa), nth0(1,L,bbb), nth0(2,L,ccc). L = [aaa, bbb, ccc].
      
      





最終的にリストを決定しました。 実際、言葉で定義すると、最初の要素がaaa



、2番目がbbb



、3番目がccc



の長さ3のリストです。 これは通常と同等です:



 ?- L=[aaa,bbb,ccc]. L = [aaa, bbb, ccc].
      
      







これが意味することです。 つまり、プロローグでは、フォームの問題を解決するアプローチは非常に自然です。ソリューションのフォームを設定し、その後、ソリューションの過程で、完全に決定されるまでソリューションを明確にします。 このアプローチは、アルゴリズムの一連のアクションとしてではなく、定義として読み取られるため、ソリューション自体(コード)を宣言型にします(ここでは、必要な入出力を予約する必要があるかもしれませんが、本質的には必須です)。



したがって、水銀には論理変数はありません ! =(

つまり、水銀は部分的に定義されたデータ構造をサポートしていません。

たとえば、リストを指定する場合、そのすべての要素を定義する必要があります。

つまり、データを明確にする唯一の方法は、データを破棄することですが、それに基づいてより具体的なデータを作成することです。 リストについて話すと、未定義のリストが対応すると想像できます



 L = [no, no, no]
      
      







部分的に定義された:



 L=[yes(aaa), no, no]
      
      







しかし、完全に指定:



 L=[yes(aaa), yes(bbb), yes(ccc)].
      
      







(こんにちは、 多分データ型)。



同じ悲しい運命がVisual Prolog (論理変数の拒否、またはVisual Prologの用語での参照ドメイン)に影響することに注意する必要があります。 その理由は、これらの厳しい命令の世界ではほとんど必要ないため、コンパイラ/仮想マシンコード/実行戦略が大幅に簡素化されるためです。 コインの裏側は、一般的に置かれたタスクを大幅にやり直す必要があり、そのソリューションの表現力がはるかに低くなることです。 ただし、人気のある言語Haskell、OCamlは、これらの機能がなくても完全に動作します=)



このアプローチに基づいて、水銀に関する論理プログラミングのアイデアが基づいています。

このアプローチでは、 論理的統一をコードレベルでもプログラミングする必要があることに注意してください(プロローグでは、出力マシンのレベルです)。



ロジックヘルパーモジュール(mercuryはhaskellなどの型クラスをサポートしています):



 :- module logic. :- interface. :- import_module list, maybe. :- typeclass unifiable(T) where [ func unify(T, T) = T is semidet ]. :- instance unifiable(maybe(T)). :- instance unifiable(list(T)) <= unifiable(T). :- pred unify(T, T, T) <= unifiable(T). :- mode unify(in, in, out) is semidet. :- pred member(T, T, list(T), list(T)) <= unifiable(T). :- mode member(in, out, in, out) is nondet. :- pred member(T, list(T), list(T)) <= unifiable(T). :- mode member(in, in, out) is nondet. :- implementation. :- import_module maybe, list. :- instance unifiable(maybe(T)) where [ func(unify/2) is unify_maybe ]. :- instance unifiable(list(T)) <= unifiable(T) where [ func(unify/2) is unify_lists ]. :- func unify_maybe(maybe(T), maybe(T)) = maybe(T) is semidet. unify_maybe(no, yes(E)) = yes(E). unify_maybe(yes(E), no) = yes(E). unify_maybe(no, no) = no. unify_maybe(yes(E), yes(E)) = yes(E). :- func unify_lists(list(T), list(T)) = list(T) is semidet <= unifiable(T). unify_lists([], []) = []. unify_lists([H|T], [H1|T1]) = [unify(H, H1) | unify_lists(T, T1)]. :- pred unify_lists(list(T), list(T), list(T)) <= unifiable(T). :- mode unify_lists(in, in, out) is semidet. unify_lists(L1, L2, unify_lists(L1, L2)). unify(A, B, unify(A, B)). member(E, E, [], []) :- fail. member(E0, E1, [H | T], [H1 | T1]) :- ( H0 = unify(E0, H), H1 = H0, E1 = H0, T=T1 ; H1 = H, member(E0, E1, T, T1) ). member(E, !L) :- member(E,_,!L).
      
      







さて、 ゼブラパズルソリューション自体:



 :- module einstein. :- interface. :- import_module io. :- pred main(io::di, io::uo) is det. :- implementation. :- import_module maybe, list, solutions, logic. :- type house ---> house(maybe(nationality), maybe(color), maybe(pet), maybe(cigarettes), maybe(drink)). :- type nationality ---> englishman; spaniard; norwegian; ukrainian; japanese. :- type color ---> red; yellow; blue; green; ivory. :- type pet ---> dog; snails; fox; horse; zebra. :- type cigarettes ---> kools; chesterfields; winston; lucky_strike; parliaments. :- type drink ---> orange_juice; tea; coffee; milk; water. :- instance unifiable(house) where [ unify( house(N, C, P, S, D), house(N1, C1, P1, S1, D1)) = house(unify(N, N1), unify(C, C1), unify(P, P1), unify(S, S1), unify(D, D1)) ]. unknown_house = house(no,no,no,no,no). solve(!Street):- % The Englishman lives in the red house logic.member(house(yes(englishman),yes(red),no,no,no), !Street), % The Spaniard owns the dog logic.member(house(yes(spaniard),no,yes(dog),no,no), !Street), % The Norwegian lives in the first house on the left unify([house(yes(norwegian),no,no,no,no),unknown_house,unknown_house,unknown_house,unknown_house], !Street), % Kools are smoked in the yellow house. logic.member(house(no,yes(yellow),no,yes(kools),no), !Street), % The man who smokes Chesterfields lives in the house % next to the man with the fox. next(house(no,no,yes(fox),no,no), house(no,no,no,yes(chesterfields),no), !Street), % The Norwegian lives next to the blue house next(house(yes(norwegian),no,no,no,no), house(no,yes(blue),no,no,no), !Street), % The Winston smoker owns snails. logic.member(house(no,no,yes(snails),yes(winston),no), !Street), % The lucky strike smoker drinks orange juice logic.member(house(no,no,no,yes(lucky_strike),yes(orange_juice)), !Street), % The Ukrainian drinks tea logic.member(house(yes(ukrainian),no,no,no,yes(tea)), !Street), % The Japanese smokes parliaments logic.member(house(yes(japanese),no,no,yes(parliaments),no), !Street), % Kools are smoked in the house next to the house where the horse is kept. next(house(no,no,yes(horse),no,no), house(no,no,no,yes(kools),no), !Street), % Coffee is drunk in the green house logic.member(house(no,yes(green),no,no,yes(coffee)), !Street), % The green house is immediately to the right (your right) of the ivory house left(house(no,yes(ivory),no,no,no), house(no,yes(green),no,no,no), !Street), % Milk is drunk in the middle house. unify([unknown_house,unknown_house,house(no,no,no,no,yes(milk)),unknown_house,unknown_house], !Street), % And, finally, zebra and water :) logic.member(house(no,no,yes(zebra),no,no), !Street), logic.member(house(no,no,no,no,yes(water)), !Street). next(H1, H2, !Street):- left(H1, H2, !Street); left(H2, H1, !Street). left(H1, H2, !Street):- unify([H1,H2,unknown_house,unknown_house,unknown_house], !Street); unify([unknown_house,H1,H2,unknown_house,unknown_house], !Street); unify([unknown_house,unknown_house,H1,H2,unknown_house], !Street); unify([unknown_house,unknown_house,unknown_house,H1,H2], !Street). main --> { solutions(pred(S::out) is nondet :- solve([unknown_house,unknown_house,unknown_house,unknown_house,unknown_house], S), L)}, print_solutions(L). print_solutions(L) --> write_string("Total solutions: "), write_int(length(L)), nl, nl, print_every_sol(L). print_every_sol([]) --> []. print_every_sol([S|SS]) --> print_sol(S), print_every_sol(SS). print_sol([]) --> []. print_sol([H|HH]) --> print_house(H), nl, print_sol(HH). print_maybe(no) --> write_string("unknown"). print_maybe(yes(T)) --> write(T). print_house(house(N, C, P, S, D)) --> write_string("house("), print_maybe(N), write_string(", "), print_maybe(C), write_string(", "), print_maybe(P), write_string(", "), print_maybe(S), write_string(", "), print_maybe(D), write_string(")").
      
      







ご覧のとおり、プロローグソリューションとの類似性は肉眼で確認できますが、コードは1桁大きくなっていますが、はい... =)



$ time ./einstein

Total solutions: 1



house(norwegian, yellow, fox, kools, water)

house(ukrainian, blue, horse, chesterfields, tea)

house(englishman, red, snails, winston, milk)

house(spaniard, ivory, dog, lucky_strike, orange_juice)

house(japanese, green, zebra, parliaments, coffee)



real 0m0.031s

user 0m0.015s

sys 0m0.015s







All Articles