【问题标题】:Solving a puzzle in Racket在 Racket 中解决难题
【发布时间】:2016-08-08 22:52:31
【问题描述】:

我尝试使用 Racket 解决这个难题 https://puzzling.stackexchange.com/questions/40094/who-committed-the-crime

一个人作案,犯罪嫌疑人5人。每个 嫌疑人在测谎仪下被问及他们认为谁犯了罪。

他们的答案如下:

Terry : It wasn't Carl, It was Steve 
Steve : It wasn't Matt, It wasn't Carl 
Matt : It was Carl, It wasn't Terry 
Ben : It was Matt, It was Steve 
Carl : It was Ben, It wasn't Terry 

测谎仪显示 每个嫌疑人都说了一个谎言和一个真相。谁犯了罪?

以下是我的代码:

(define (oneof a b)
  (or (and a (not b)) (and b (not a))))

(for ((i 5))
  (define templist (list #f #f #f #f #f))  ; make a temporary list of all false; 
  (set! templist (list-set templist i #t)) ; one by one keep one as true in this loop; 
  (define t (list-ref templist 0))   ; allocate each person according to above list (one kept true one by one)
  (define s (list-ref templist 1))
  (define m (list-ref templist 2))
  (define b (list-ref templist 3))
  (define c (list-ref templist 4))

  (when                              ; test if all statements fit with above assignment: 
    (and
     (oneof  (not c) s)              ; Terry's statement
     (oneof  (not m) (not c))        ; Steve's statement
     (oneof  c (not t))              ; Matt's statement
     (oneof  m s)                    ; Ben's statement
     (oneof  b (not t)))             ; Carl's statement
    (println (list "t" "s" "m" "b" "c"))  ; print allocation if all statement fit in; 
    (println templist)))

输出表明马特犯了罪:

'("t" "s" "m" "b" "c")
'(#f #f #t #f #f)

它可以工作,但代码是命令式的并且不是很实用(尤其是定义 t、定义 s、... 部分)。如何改进?感谢您的 cmets/答案。

【问题讨论】:

  • 代码审查 SO 会更合适。

标签: scheme racket


【解决方案1】:

此答案基于@Oscar Lopez 的答案,但经过修改以使用更多惯用功能,例如带有辅助函数的filter,而不是forwhenprintln

这种返回值的代码风格更有用,因为它产生的值可以在以后的代码中使用,而像println 这样的命令式操作的可重用性要小得多。

@Oscar Lopez 的make-matrix 函数会生成一个初始可能性列表,但我们必须过滤掉那些不可能的,只留下那些可能的。所以写下来:

;; If there are no valid solutions, this will be an empty list,
;; if there's one, this will be a list of one element,
;; and if there are more, this will include all of them.
(filter valid-solution? (make-matrix 5))

;; Wish list:
;; valid-solution? : (Listof Boolean) -> Boolean

现在我们只需要为valid-solution?添加一个定义

;; valid-solution? : (Listof Boolean) -> Boolean
;; Given a list containing booleans for whether Terry, Steve, Matt, Ben, and Carl did it,
;; this determines whether that combination is possible under the constraints in the problem.
(define (valid-solution? row)
  ; unpack each row into the corresponding variables
  (match-define (list t s m b c) row)
  ; don't reinvent the wheel, `oneof` is called `xor`
  (and (xor (not c) s)
       (xor (not m) (not c))
       (xor c (not t))
       (xor m s)
       (xor b (not t))))

就是这样。包括make-matrix的完整程序是这样的:

#lang racket

;; make-matrix : Natural -> (Listof (Listof Boolean))
;; generate a matrix with possibilities
(define (make-matrix n)
  (for/list ([i (in-range n)])
    (build-list n (λ (j) (= i j)))))

;; valid-solution? : (Listof Boolean) -> Boolean
;; Given a list containing booleans for whether Terry, Steve, Matt, Ben, and Carl did it,
;; this determines whether that combination is possible under the constraints in the problem.
(define (valid-solution? row)
  ; unpack each row into the corresponding variables
  (match-define (list t s m b c) row)
  ; don't reinvent the wheel, `oneof` is called `xor`
  (and (xor (not c) s)
       (xor (not m) (not c))
       (xor c (not t))
       (xor m s)
       (xor b (not t))))

;; If there are no valid solutions, this will be an empty list,
;; if there's one, this will be a list of one element,
;; and if there are more, this will include all of them.
(filter valid-solution? (make-matrix 5))

【讨论】:

  • 功能强大的解决方案。
【解决方案2】:

这是一个用惯用 Racket 编写的等效程序 - 并避免了所有那些在编写函数式代码时不受欢迎的讨厌的 set!list-ref

; generate a matrix with possibilities
(define (make-matrix n)
  (for/list [(i (in-range n))]
    (build-list n (λ (j) (= i j)))))

; iterate over each row
(for [(row (make-matrix 5))]
  ; unpack each row into the corresponding variables
  (match-let ([(list t s m b c) row])
    ; don't reinvent the wheel, `oneof` is called `xor`
    (when (and (xor (not c) s)
               (xor (not m) (not c))
               (xor c (not t))
               (xor m s)
               (xor b (not t)))
      (println '(t s m b c))
      (println row))))

【讨论】:

  • 这里有很多新功能!
  • 研究文档,你会发现你在命令式代码中习惯的所有东西在函数式代码中都有一个更好、更精简的等价物
  • 一个更惯用的解决方案是使用for/list而不是forcond而不是when,返回row而不是打印它,如果条件不是则返回false' t 满意,因此围绕整个事物的 (filter identity ...) 返回所有有效的可能性。
  • @AlexKnauth :如果您输入它作为答案会更清楚。
猜你喜欢
  • 2023-02-02
  • 1970-01-01
  • 1970-01-01
  • 2012-05-10
  • 1970-01-01
  • 2020-04-12
  • 2014-06-06
  • 2023-04-01
  • 1970-01-01
相关资源
最近更新 更多