Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- ###############################################
- -- Rule-based FizzBuzz version 0.1
- -- Written by hevohevo, License: MIT
- -- Twitter: @hevohevo
- -- http://hevohevo.hatena.com/
- -- 僕の考えた、最も不適切なFizzBuzzプログラムです。
- -- コンセプトは、「LuaでAI実装して推論によってFizzBuzzを解かせた」(嘘は言ってない)
- -- つまりは、なんっちゃってRule-based SystemのLua実装です。
- -- 効率皆無で自己満足以外の何者でもないけれど、今回のお題的にはこれでよい・・・・・・はず。
- -- プログラムをそのまま実行すると、FizzBuzz仕様通りの出力を行います。
- -- オプション"step_option=true"に書き換えて実行するとRBSは推論内容を表示します。
- -- 裏でどんな推論やっているのか気になった方には後者をおすすめします。
- -- ###############################################
- -- Options
- -- falseでFizzBuzz仕様どおりの出力
- -- trueでステップ実行(Ideoneだとキー入力待たずに一気に出力されます)。
- local step_option = false
- -- ###############################################
- -- Classes and Functions
- -- WM Class
- WM = {}
- function WM.new(init_table)
- local obj = {}
- -- 命題を蓄えておくテーブル
- obj.facts = init_table
- -- WMからキーを元に命題を探す
- obj.find = function(self, key)
- return self.facts[key]
- end
- -- WMに命題を加える
- obj.add = function(self, key, value)
- value = value or true
- self.facts[key]=value
- return true
- end
- -- WMから命題を消す
- obj.delete = function(self, key)
- self.facts[key]=nil
- return true
- end
- -- WMに命題"terminated"を加える
- obj.terminate = function(self)
- return self:add("terminated")
- end
- -- WMに命題"terminated"があるか調べる(あると推論終了)
- obj.isTerminated = function(self)
- return self:find("terminated")
- end
- return obj
- end
- -- Rule Class
- Rule = {}
- function Rule.new(opts)
- local obj = {}
- obj.name = opts.name -- ルールは一意な名前を持つ
- obj.priority = opts.priority or 0 -- 値が小さい方が優先度は高い。標準は0
- obj.cond = opts.cond -- ルールの条件部はbooleanを返す関数(true=ルール実行可能、false=不可)
- obj.run = opts.run -- ルールの実行部はbooleanを返す関数(true=正常実行完了)
- obj.fired_log = {0} -- このルールが発火したステップ番号を配列に蓄積
- -- 発火履歴を書き込む
- obj.addLog = function(self, step)
- table.insert(self.fired_log, step)
- end
- -- 発火履歴の末尾を取り出す(直近の発火したステップ数)
- obj.tailLog = function(self)
- return self.fired_log[#self.fired_log]
- end
- return obj
- end
- -- RuleDB Class
- RuleDB = {}
- function RuleDB.new(init_table)
- local obj = {}
- -- init_tableによって与えられたデータを使ってルール作成・蓄積
- obj.rules = {}
- for i,v in ipairs(init_table) do
- obj.rules[i] = Rule.new(v)
- end
- return obj
- end
- -- すべてのルールの条件部を調べ、現在、発火可能なルールをすべて集める
- function collectTriggeredRules(_rule_db, _wm)
- local results = {}
- for _, rule in ipairs(_rule_db.rules) do
- if rule.cond(_wm) then
- table.insert(results, rule)
- end
- end
- return results
- end
- -- 発火可能なルール群の中で最も優先すべきルールを1つ返す
- -- 「競合解消戦略」は以下の通り
- -- 1. priority値が小さいほうが優先度高い(デフォルト0、負も可)
- -- 2. priority値が同じならば、最近発火したルールは後回し
- -- 3. 発火履歴も同じならば、ルール登録順
- function resolveConflict(triggered_rules)
- local best_rule = triggered_rules[1] -- 先頭のルールを代入
- for i, rule in ipairs(triggered_rules) do
- if i>1 then -- 2番目以降と比較
- if best_rule.priority > rule.priority then
- best_rule = rule -- 優先度の高い(値が小さい)方を選択
- elseif best_rule.priority == rule.priority then
- if best_rule:tailLog() > rule:tailLog() then
- best_rule = rule
- end
- end
- end
- end
- return best_rule
- end
- -- ジェネリックfor文を使って独自のイテレータを作る
- -- 返し値の先頭がnilのときにジェネリックfor文は停止する
- -- (参考:http://qiita.com/hevo2/items/604b5405a981fd5fd207)
- function RBEngine(_rule_db, _wm, max_step)
- local max_step = max_step or 500
- local step = 0
- return function()
- local fired_rule = nil
- local triggered_rule = {}
- step = step + 1
- -- WMに "terminated" があるか、最大ステップ数を超えているので終了
- if _wm:isTerminated() or (step > max_step) then
- fired_rule = nil
- return fired_rule, step, triggered_rule, _wm
- end
- -- 発火可能なルールをすべて集める
- triggered_rules = collectTriggeredRules(_rule_db, _wm)
- if type(triggered_rules) == "table" and #triggered_rules > 0 then
- -- 発火可能ルール群の中からもっとも優先するルールを取り出す(競合解消)
- fired_rule = resolveConflict(triggered_rules)
- assert(fired_rule.run(_wm)) -- run部の実行時にtrueが返ってこなかったらエラー
- fired_rule:addLog(step) -- ルールに発火履歴を書き込み
- else
- -- 発火可能なルールがない
- fired_rule = nil
- end
- return fired_rule, step, triggered_rules, _wm
- end
- end
- -- #############################################
- -- Rule-base System のデータ部分
- -- Working Memory
- -- key=value
- data_wm = {
- num=1
- }
- -- Rule DataBase
- -- rule = {
- -- name, ルールは一意な名前を持つ
- -- priority, 値が小さい方が優先度は高い。標準は0
- -- cond, ルールの条件部はbooleanを返す関数(true=ルール実行可能、false=不可)
- -- run, ルールの実行部はbooleanを返す関数(true=正常実行完了, その他異常終了)
- -- fired_log このルールが発火したステップ番号を配列に蓄積
- -- }
- data_rules = {
- { name = "terminated",
- cond = function(wm) return wm:find("num") > 100 end,
- run = function(wm) return wm:terminate() end,
- priority = -1 },
- { name = "fizz",
- cond = function(wm) return (wm:find("num") % 3) == 0 end,
- run = function(wm) return wm:add("say", "Fizz") end,
- priority = 1 },
- { name = "buzz",
- cond = function(wm) return (wm:find("num") % 5) == 0 end,
- run = function(wm) return wm:add("say", "Buzz") end,
- priority = 1 },
- { name = "fizzbuzz",
- cond = function(wm) return (wm:find("num") % 15) == 0 end,
- run = function(wm) return wm:add("say", "FizzBuzz") end,
- priority = 0 },
- { name = "others",
- cond = function(wm) return true end,
- run = function(wm) return wm:add("say", wm:find("num")) end,
- priority = 2 },
- { name = "say",
- cond = function(wm) return wm:find("say") end,
- run = function(wm)
- io.write(wm:find("say"))
- wm:delete("say")
- wm:add("said")
- return true
- end,
- priority = 0 },
- { name = "increase",
- cond = function(wm) return wm:find("said") end,
- run = function(wm)
- wm:add("num", wm:find("num") + 1)
- wm:delete("said")
- return true
- end,
- priority = 0 }
- }
- -- ###########################################
- -- Main
- -- 標準入力から実行オプションを入手(Ideoneだとうまくいかない?)
- local args = {...}
- if args[1]=="step" then
- step_option = true
- end
- -- WorkingMemory と Rule DBを作成
- local wm = WM.new(data_wm)
- local rule_db = RuleDB.new(data_rules)
- -- ジェネリックFor文で1ステップずつ推論を進めていく。
- for fired_rule, step, triggered_rules, current_wm in RBEngine(rule_db, wm) do
- if step_option then
- -- 1ステップずつ推論
- print()
- io.read()
- print("step: "..step)
- print(" number:"..current_wm:find("num"))
- print(" fired_rule: "..fired_rule.name)
- print()
- else
- -- FizzBuzz仕様通りの出力
- end
- end
- --print()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement