使用Ruby amb解决说谎者谜题

    说谎者谜题是sicp4.3.2小节的一道题目,题目本身不难:
五个女生参加一个考试,她们的家长对考试结果过分关注。为此她们约定,在给家里写信谈到考试的时候,每个姑娘都要写一句真话和一句假话。下面是从她们的信里摘抄出来的句子:
Betty : kitty考第二,我只考了第三
Ethel : 你们应该很高兴听到我考了第一,joan第二
joan :   我考第三,可怜的Ethel垫底
kitty:  我第二,marry只考了第四
marry: 我是第四,Betty的成绩最高。
这五个姑娘的实际排名是什么?

    Ruby本来就有call/cc,因此也可以实现amb操作符,网上已经有一个实现了:

class Amb
  class ExhaustedError < RuntimeError; end
  def initialize
    @fail = proc { fail ExhaustedError, "amb tree exhausted" }
  end
  def choose(*choices)
    prev_fail = @fail
    callcc { |sk|
      choices.each { |choice|
        callcc { |fk|
          @fail = proc {
            @fail = prev_fail
            fk.call(:fail)
          }
          if choice.respond_to? :call
            sk.call(choice.call)
          else
            sk.call(choice)
          end
        }
      }
      @fail.call
    }
  end
  def failure
    choose
  end
  
  def assert(cond)
    failure unless cond
  end
  alias :require :assert
end

    这一段代码与scheme宏实现amb是完全相同的:

(define amb-fail '*)

(define initialize-amb-fail
  (lambda ()
    (set! amb-fail
      (lambda ()
        (error "amb tree exhausted")))))

(initialize-amb-fail)
(define call/cc call-with-current-continuation)
(define-syntax amb
  (syntax-rules ()
    ((amb alt )
     (let ((prev-amb-fail amb-fail))
       (call/cc
        (lambda (sk)

          (call/cc
           (lambda (fk)
             (set! amb-fail
                   (lambda ()
                     (set! amb-fail prev-amb-fail)
                     (fk 'fail)))
             (sk alt))) 
             
             (prev-amb-fail)))))))

    回到谜题,从题意可知每个姑娘的两句话的异或结果为true,并且姑娘的排名肯定不会相同,因此定义两个辅助过程:

require 'amb'
def distinct?(items)
  items.uniq==items
end
def xor(exp1,exp2)
 (exp1 or exp2) and !(exp1 and exp2)
end

    剩下的完全就是将题目翻译成代码即可了,没有多少可以解释的东西:

amb=Amb.new
betty=amb.choose(*[1,2,3,4,5])
ethel=amb.choose(*[1,2,3,4,5])
joan=amb.choose(*[1,2,3,4,5])
kitty=amb.choose(*[1,2,3,4,5])
marry=amb.choose(*[1,2,3,4,5])

amb.require(xor(kitty==2,betty==3))
amb.require(xor(ethel==1,joan==2))
amb.require(xor(joan==3,ethel==5))
amb.require(xor(kitty==2,marry==4))
amb.require(xor(marry==4,betty==1))
amb.require(distinct?([betty,ethel,joan,kitty,marry]))
puts "betty:#{betty} ethel:#{ethel} joan:#{joan} kitty:#{kitty} marry:#{marry}"

    答案就是:
    betty:3 ethel:5 joan:2 kitty:1 marry:4  

    最后给出一个Prolog的解答:

notmember(A,[]).
notmember(A,[B|L]):-
  A\==B,
  notmember(A,L).
distinct([A,B,C,D,E]):-
   notmember(A,[B,C,D,E]),
   notmember(B,[A,C,D,E]),
   notmember(C,[A,B,D,E]),
   notmember(D,[A,B,C,E]),
   notmember(E,[A,B,C,D]).
xor(Exp1,Exp2):-
   (Exp1;Exp2),\+ (Exp1,Exp2).
solve(Betty,Ethel,Joan,Kitty,Marry):-  
   X=[1,2,3,4,5],
   member(Betty,X),
   member(Ethel,X),
   member(Joan,X),
   member(Kitty,X),
   member(Marry,X),
   distinct([Betty,Ethel,Joan,Kitty,Marry]),
   xor(Kitty=:=2,Betty=:=3),
   xor(Ethel=:=1,Joan=:=2),
   xor(Joan=:=3,Ethel=:=5),
   xor(Kitty=:=2,Marry=:=4),
   xor(Marry=:=4,Betty=:=1).

文章转自庄周梦蝶  ,原文发布时间2008-11-15

时间: 2024-08-01 17:35:04

使用Ruby amb解决说谎者谜题的相关文章

amb解决排列组合问题

看到这么一个题目:     {3,2,2,6,7,8}排序输出,7不在第二位,68不在一起.     这样的题目似乎避免不了遍历,关键还在于过滤条件的安排,怎么让过滤的范围尽量地小.通常的做法是循环遍历,对于类似Prolog这样的语言来说,由于内置了推理引擎,可以简单地描述问题,让引擎来帮你做递归遍历,解决这类问题是非常简单的.Prolog好久没写,以Ruby的amb操作符为例来解决这道题目: #结果为hash,去重$hash={} amb=Amb.new array=[3,2,2,6,7,8]

服务器-ruby怎么解决windows和linux路径差异问题

问题描述 ruby怎么解决windows和linux路径差异问题 比如说想要取一个绝对路径下的某个文件,本地是windows环境,路径用"", 但服务器是linux环境,路径需要用"/",现在的解决方法是判断RUBY_PLATFORM,然后给了2个不同的路径,但总感觉这样的实现方式不太好,ruby中有没有类似File.PATH_SEPARATOR这种东西呢,可以自动判定运行环境然后决定使用哪个分隔符. 解决方案 linux和windows 中 File.separa

Python vs Ruby: 谁是最好的 web 开发语言?

Python 和 Ruby 都是目前用来开发 websites.web-based apps 和 web services 的流行编程语言之一. 这两种语言在许多方面有相似之处.它们都是高级的面向对象的编程语言,都是交互式脚本语言.都提供标准库且支持持久化.但是,Python 和 Ruby 的解决方法却千差万别,因为它们的发展历程导致它们的语法和原理都大不相同. 那么要采用哪种语言来进行编程就需要好好考虑了,因为每种语言都是自己的优点和缺点.而且这些优缺点都会影响我们的决定. 基础知识 Pyth

Java 解惑: 你能通过改变一个单词来解决这个bug吗?

作为<Java解惑>这本书的脑残粉,我们一直想要寻找新的java谜题以满足我们的好奇心.但是很长时间以来,我们并没有搜集到比较满意的题目,所以为了向书中那些谜题致敬,我们决定自己造一个.解谜的好处就像书中所说的"一旦你解决了这些谜题,那些曾经愚弄过最具经验的Java程序员的隐晦且有违直觉的语言行为,将再也无法把你骗过",除此之外,解谜过程所带给我们的快乐也是难以比拟的.为了增加解谜的乐趣,我们决定从能完美解决问题的获胜者中挑选出一位,奖励其250美元的Amazon礼品卡.赶

Ruby的对象模型

  Ruby的对象模型,包含在下面这张图中:     首先要知道,Ruby中的类也是对象,类相比于其他对象特殊的地方在于能够产生对象,既然类是对象,那么它显然也有类,也就是所谓类的类,这个类的类在Ruby中就是类的metaclass,图中的(OtherClass),(OtherClass)就是类OtherClass的klass(C层次),(OtherClass)存储了类的方法(类方法)和类的实例变量,并且是唯一的且不可实例化.在Ruby层次上我们想操作(otherclass)应该类似:    c

在游戏中学习:程序员如何提升自己的安全技能?

本文讲的是在游戏中学习:程序员如何提升自己的安全技能?,解决编程谜题游戏是提高开发逻辑和解决问题能力的一种有趣手段.此外,当你尝试熟悉一门新的编程语言时,解决这类编程语言的谜题游戏可以帮助你加快学习过程. 无论你是一个编程新手,还是想要增强你现有的技能,或渴望掌握最热门的编码语言,以下文章中介绍的10 款最流行的编程谜题益智网站,都将帮助你测试自己的思维,以及改善你的编程.解决问题与逻辑思维的能力. 1. Programming Praxis 如果你正在寻找一个有趣的方式来提高你的编程能力,Pr

sicp 4.3.2部分习题

4.38,谜题就有翻译错误,问题更是错的离谱.原题是这样的: Baker, Cooper, Fletcher, Miller, and Smith live on different floors of an apartment house that contains only five floors. Baker does not live on the top floor. Cooper does not live on the bottom floor. Fletcher does not

游戏设计中的锁机制

这篇文章是关于游戏设计中一个普通细节的,我把它称为"锁"机制.尽管"锁"这种概念在很多类型的游戏中都会出现,但是它对于冒险游戏和角色扮演游戏(RPG)来说有着非同小可的作用. 游戏设计师为了做出高品质的RPG或冒险游戏,往往需要做大量的工作.必须架构一个完整的世界.如果一个玩家可以操作他的角色不受限制的穿越整个世界,那么他必然会错过很多精心设计的地区. 为了避免这个问题,设计师在游戏中加入了"锁"的概念.这就保证了玩家在探索完足够的区域后才能进入

《精益软件度量——实践者的观察与思考》—第1章1.1节精益软件开发的度量体系

第1章 度量谜题 精益软件度量--实践者的观察与思考 "我们所能拥有的最美好的经历是感受到神秘,它是触发所有真正艺术和科学起源的基本情感." 艾尔伯特·爱因斯坦(1879-1955) 按照IEEE的定义,"软件工程是将系统化.规则,以及可控的体系方法,应用于软件设计.开发.操作和维护:换言之,即工程理念在软件中的贯彻."1看上去很美,不是吗?当我们看到一个又一个软件开发组织,特别是大型的组织,特别是拥有辉煌历史的组织,把过程可控作为主要的管理目标时,一次又一次地惊讶