【问题标题】:Scheme - Blackjack Program (SICP) / Learning to program方案 - 二十一点计划 (SICP) / 学习编程
【发布时间】:2012-03-15 02:59:28
【问题描述】:

我决定学习编程阅读/做 SICP。

我正在使用 DrRacket 和 http://www.neilvandyke.org/racket-sicp/

我写了一个二十一点程序https://github.com/fnava621/scheme_blackjack。它有效。

你能让这个程序更易读更简洁吗?

我放太多cmets了吗?没有足够的cmets?如何让这个程序“更好”?

我也放弃了挑战。你能制作一个程序,使用“最优”策略并确定玩家赢庄家的概率(使用 n 个样本大小)吗?我也会这样做并比较代码。

感谢您阅读/做, 费尔南多·纳瓦

【问题讨论】:

  • 也许你应该尝试用更难的方案变体来编写它。球拍完全不同,更容易。我个人不喜欢“如果”。我可以阅读并理解整个程序。

标签: scheme racket sicp


【解决方案1】:

一些建议:

  1. 避免使用太多的突变,即set!
  2. 避免重新定义内置函数,例如length
  3. 为每个函数添加签名和目的声明。有关说明,请参阅 here
  4. 编写测试!尝试使用rackunit,记录在here

【讨论】:

    【解决方案2】:

    让我专注于单个函数show-deck。我知道它在做什么,但是可以稍微简化递归。使其更易于阅读可能会稍微降低效率,但我们在这里谈论的是 52 张卡片...... :)

    内循环和外循环纠缠在原代码中。下面是一个解开它们的版本:

    (define (show-deck1 first-list second-list)
      (define (outer-loop first-list second-list)
        (cond
          ((null? first-list)
           '())
          (else
           (append (inner-loop (car first-list) second-list)
                   (outer-loop (cdr first-list) second-list)))))
    
      (define (inner-loop x second-list)
        (cond
          ((null? second-list)
           '())
          (else
           (cons (cons x (car second-list))
                 (inner-loop x (cdr second-list))))))
    
      (outer-loop first-list second-list))
    

    我们可以做一个简化:用这种方式表达的定义,更容易看出map可以用来做inner-loop

    (define (show-deck2 first-list second-list)
      (cond
        ((null? first-list)
         '())
        (else
         (define x (car first-list))
         (append (map (lambda (y) (cons x y)) second-list)
                 (show-deck2 (cdr first-list) second-list)))))
    

    这使得更容易看到外部迭代的结构。我们可以更进一步,将map 用于内部和外部循环,并使用(apply append ...) 来展平map 的嵌套使用引入的子结构:

    (define (show-deck3 first-list second-list)
      (apply append 
             (map (lambda (x)
                    (map (lambda (y) (cons x y)) second-list))
                  first-list)))
    

    您的版本通过巧妙地线程化计算完全避免了(apply append ...) 的东西,但它以牺牲一些可读性为代价。避免(apply append ...) 并仍能轻松查看嵌套循环结构的一种方法是使用foldr 方法而不是map 方法:

    (define (foldr f acc l)
      (cond
        ((null? l) acc)
        (else 
         (f (car l)
            (foldr f acc (cdr l))))))
    
    (define (show-deck first-list second-list) 
      (foldr (lambda (x acc)
               (foldr (lambda (y acc)
                        (cons (cons x y) acc))
                      acc
                      second-list))
             '()
             first-list))
    

    这应该与您的原始代码之前所做的一致。但是,它将循环降级为foldr,因此所有iter 函数都消失在foldr 的使用中。

    就我个人而言,如果我用完整的 Racket 编写代码,我会使用 for 循环对其进行编码。 :) 它看起来像这样:

    ;; If we're allowed to use full Racket:
    (define (show-deck first-list second-list)
      (for*/list ([x first-list]
                  [y second-list])
        (cons x y)))
    

    【讨论】:

      猜你喜欢
      • 2012-01-03
      • 2020-05-16
      • 2011-11-23
      • 2011-02-02
      • 2018-09-10
      • 2010-09-11
      • 2017-12-20
      • 1970-01-01
      • 2014-11-18
      相关资源
      最近更新 更多