ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

働くパパのためのThe General Problem Solver

Paradigms of Artificial Intelligence Programming: Case Studies in Common Lispの4章から。4章の一番最初に出てきたプログラムをPythonで実装してみました。元のlispソースはhttp://norvig.com/paip/gps1.lispです。教科書は息子を学校に連れて行くパパでしたが、家族が寝た後に家事をするパパを実装しました。

人工知能様が子供を寝かしつけた後の残りの家事を見て、どうすればパパもおやすみ状態に持って行けるかを教えてくれます。

動作例

洗い物だけ残っている時

洗い物だけ残っている時は、こんな感じで問い合わせることができます。

gps((u'消灯done', u'洗い物残ってる', u'洗濯物done', u'明日のご飯done'),
	u'おやすみ',
	house_ops)

結果はこうです。

execute  洗い物する
execute  家事完了
execute  寝る
True

洗い物だけすれば、寝る事ができます。余裕ですね。

洗濯物と洗い物が残っている時

洗濯物と洗い物が残っている時はこんな感じです。

gps((u'TV消すdone',  u'空調切るdone', u'電気ついてる', 
	u'洗い物残ってる', u'明日のご飯done', 
	u'洗濯物取り入れてない', u'洗濯物たためてない', u'洗濯できてない', u'洗濯物干せてない'),
	u'おやすみ',
	house_ops)

結果はこうです。

execute  洗い物する
execute  洗濯物取り入れる
execute  洗濯物たたむ
execute  洗濯機を回す
execute  洗濯物干す
execute  洗濯物完了
execute  家事完了
execute  電気消す
execute  消灯完了
execute  寝る
True

洗濯が大変で、12時超えコースです。

何もかも残っているとき

何もかも残っているとき><

print gps((u'TVついてる',  u'空調ついてる', u'電気ついてる',
           u'洗い物残ってる', u'ご飯が無い', 
           u'洗濯物取り入れてない', u'洗濯物たためてない', u'洗濯できてない', u'洗濯物干せてない'),
          u'おやすみ',
          house_ops)

結果はこう。

execute  洗い物する
execute  洗濯物取り入れる
execute  洗濯物たたむ
execute  洗濯機を回す
execute  洗濯物干す
execute  洗濯物完了
execute  明日のご飯作る
execute  家事完了
execute  TV消す
execute  空調消す
execute  電気消す
execute  消灯完了
execute  寝る
True

パパ泣きたい。

ソースコード

gps.py 単体で起動すると教科書の問題を解きます。

# -*- coding: utf-8 -*-

g_state = []
g_ops = []

# operatorの定義
class operator:
    def __init__(self, action, preconds, add_list, del_list = ()):
        self.action = action
        self.preconds = preconds
        self.add_list = add_list
        self.del_list = del_list

# lispの関数
def some(f, l):
    """list l の要素に対してfを呼び出し、論理和を取って結果を返す"""
    return reduce(lambda x, y: x or y, map(f, l), False)

def every(f, l):
    """list l の要素に対してfを呼び出し、論理積を取って結果を返す"""
    return reduce(lambda x, y: x and y, map(f, l))

def currying(f, x):
    """引数を2つ取る関数fから、一つ目の引数をxに固定した関数を作る"""
    return lambda y: f(x, y)

def union(x, y):
    """引数の和集合を取る"""
    return list(set(x).union(set(y)))

def difference(x, y):
    """引数の差集合を取る"""
    return list(set(x).difference(set(y)))

def find_all(item, seq, test_func):
    find = currying(test_func, item)
    return filter(find, seq)
    
# The General Problem Solver
def gps(state, goals, ops):
    global g_state
    global g_ops
    g_state = state
    g_ops = ops
    return every(achieve, (goals,))

def achieve(goal):
    if goal in g_state: # g_stateにgoalが含まれていれば、goalまでに必要な全てのoperationを実行している。
        return True
    # goalを含むoperationを探す。全てのoperationのadd_listがgoalを含まない場合はFalseを返す。
    return some(apply_op, (find_all(goal, g_ops, appropriate)))

def appropriate(goal, op):
    """ opration opのadd_listにgoalがあるか(goalを実現するために必要なactionがあるか)を調べる"""
    try:
        index = op.add_list.index(goal)
        return op.add_list[index:]
    except ValueError:
        return ()

def apply_op(op):
    global g_state
    ret = False
    if every(achieve, op.preconds):
        # operation opの前提条件を全て達成しているなら、acitonを実行する。
        print "execute ",
        print op.action.encode('utf-8')
        g_state = difference(g_state, op.del_list) # del_listに含まれる状態を削除
        g_state = union(g_state, op.add_list)      # add_listに含まれる状態を追加
        ret = True
    return ret

def main():
    """教科書に出てくる例"""
    school_ops = (
        operator('drive-son-to-school', 
                 ('son-at-home', 'car-works'),
                 ('son-at-school',),
                 ('son-at-home',)),
        operator('shop-installs-battery',
                 ('car-needs-battery', 'shop-knows-problem', 'shop-has-money'),
                 ('car-works',)),
        operator('tell-shop-problem',
                 ('in-communication-with-shop',),
                 ('shop-knows-problem',),),
        operator('telephon-shop',
                 ('know-phone-number',),
                 ('in-communication-with-shop',)),
        operator('look-up-number',
                 ('have-phone-book',),
                 ('know-phone-number',)),
        operator('give-shop-money',
                 ('have-money',),
                 ('shop-has-money',),
                 ('have-money',))
        )

    print gps(('son-at-home','car-works'),
              'son-at-school',
              school_ops)

    print gps(('son-at-home', 'car-needs-battery', 'have-money', 'have-phone-book'),
              'son-at-school',
              school_ops)

    print gps(('son-at-home', 'car-needs-battery', 'shop-knows-problem', 'shop-has-money'),          
              'son-at-school',
              school_ops)
    
    print gps(('son-at-home', 'car-needs-battery', 'have-money'),
              'son-at-school',
              school_ops)

if __name__ == "__main__":
     main()

こちらが、私の家事を解くためのソースです。オペレータの定義と、問い合わせ部分です。

# -*- coding: utf-8 -*-
from gps import *

house_ops = (
    operator(u'寝る',
             (u'家事done', u'消灯done',),
             (u'おやすみ',)),
    operator(u'消灯完了',
             (u'TV消すdone', u'空調切るdone',u'電気消すdone'),
             (u'消灯done')),
    operator(u'TV消す',
             (u'TVついてる',),
             (u'TV消すdone',),
             (u'TVついてる')),
    operator(u'空調消す',
             (u'空調ついてる',),
             (u'空調切るdone',),
             (u'空調ついてる')),
    operator(u'電気消す',
             (u'電気ついてる',),
             (u'電気消すdone',),
             (u'電気ついてる')),
    operator(u'家事完了',
             (u'洗い物done', u'洗濯物done', u'明日のご飯done'),
             (u'家事done',)),
    operator(u'洗い物する',
             (u'洗い物残ってる',),
             (u'洗い物done',),
             (u'洗い物残ってる',)),
    operator(u'明日のご飯作る',
             (u'ご飯が無い',),
             (u'明日のご飯done',),
             (u'ご飯が無い',)),
    operator(u'洗濯物完了',
             (u'洗濯物取り入れdone', u'洗濯物たたむdone', u'洗濯done', u'洗濯物干すdone'),
             (u'洗濯物done',),
             (u'洗濯物残ってる',)),
    operator(u'洗濯物取り入れる',
             (u'洗濯物取り入れてない',),
             (u'洗濯物取り入れdone',),
             (u'洗濯物取り入れてない',)),
    operator(u'洗濯物たたむ',
             (u'洗濯物たためてない',),
             (u'洗濯物たたむdone',),
             (u'洗濯物たためてない',)),
    operator(u'洗濯機を回す',
             (u'洗濯できてない',),
             (u'洗濯done',),
             (u'洗濯できてない',)),
    operator(u'洗濯物干す',
             (u'洗濯物干せてない',),
             (u'洗濯物干すdone',),
             (u'洗濯物干せてない',)),
)

print "洗い物だけ残っている時"
print gps((u'消灯done', u'洗い物残ってる', u'洗濯物done', u'明日のご飯done'),
          u'おやすみ',
          house_ops)
print ""

print "一通り終えて、TVだけがついている時"
print gps((u'TVついてる',  u'空調切るdone', u'電気消すdone', u'家事done'),
          u'おやすみ',
          house_ops)
print ""

print "電気がついていて、洗濯物と洗い物が残っている時"
print gps((u'TV消すdone',  u'空調切るdone', u'電気ついてる', 
           u'洗い物残ってる', u'明日のご飯done', 
           u'洗濯物取り入れてない', u'洗濯物たためてない', u'洗濯できてない', u'洗濯物干せてない'),
          u'おやすみ',
          house_ops)
print ""

print "何もかも残っているとき><"
print gps((u'TVついてる',  u'空調ついてる', u'電気ついてる',
           u'洗い物残ってる', u'ご飯が無い', 
           u'洗濯物取り入れてない', u'洗濯物たためてない', u'洗濯できてない', u'洗濯物干せてない'),
          u'おやすみ',
          house_ops)