Functional programming 在某些前哨值(如-999)之前查找输入中出现的非负数的平均值

Functional programming 在某些前哨值(如-999)之前查找输入中出现的非负数的平均值,functional-programming,scheme,lisp,racket,higher-order-functions,Functional Programming,Scheme,Lisp,Racket,Higher Order Functions,在论文中提到了一个实验的编程任务 任务:“大致上,降雨要求非负数的平均值 在某些前哨值(如-999)之前的输入中出现” 我决定尝试使用高级学生语言(ASL)以HtDP方式解决这个问题 谈到有效的解决方案,我想知道是否有办法使它更优雅。 此外,还不清楚如何根据HtDP手册中介绍的设计方法对这样的函数进行模板化 以下是我的解决方案: ;; ListOfNumber Integer -> Integer ;; produce the average of numbers in the list

在论文中提到了一个实验的编程任务

任务:“大致上,降雨要求非负数的平均值 在某些前哨值(如-999)之前的输入中出现”

我决定尝试使用高级学生语言(ASL)以HtDP方式解决这个问题

谈到有效的解决方案,我想知道是否有办法使它更优雅。 此外,还不清楚如何根据HtDP手册中介绍的设计方法对这样的函数进行模板化

以下是我的解决方案:

;; ListOfNumber Integer -> Integer
;; produce the average of numbers in the list before the guard number
;; INVARIANTS:
;; - lon starts with at least one non-negative integer
;; - guard number is a member of lon
(check-expect (list-average (cons 25 (cons -999 empty)) -999) (/ 25 1))
(check-expect (list-average (cons 8 (cons 5 (cons 17 (cons -999 (cons 12 (cons 25 empty)))))) -999) (/ (+ 8 5 17) 3))

;(define (list-average lon n) 0) ;stub

(define (list-average lon n)
  (local [(define (numbers-before-n lon n)
            (cond [(or (empty? lon) (= (first lon) n)) empty]
                  [else (cons (first lon)
                              (numbers-before-n (rest lon) n))]))
          (define VALID-NUMBERS (numbers-before-n lon n))
          (define SUM (foldr + 0 VALID-NUMBERS))
          (define AVERAGE (/ SUM (length VALID-NUMBERS)))]
    AVERAGE))

我喜欢你的解决方案!然而,我认为通过使用
#lang racket
,您可以利用更多的内置过程来编写更简短、更惯用的答案:

(define (list-average lon n)
  (let ([valid (takef lon (negate (curry = n)))])
    (/ (apply + valid) (length valid))))
说明:

  • takef
    将获取列表中的所有元素,直到满足谓词指定的条件。在本例中,谓词是
    (lambda(x)(not(=nx))
    ,但即使这样也可以简化-继续阅读
  • curry
    将接受一个函数和一个参数,并生成一个设置了该参数的新函数,但仍希望使用另一个参数调用该函数
  • 否定
    将返回谓词的否定
  • apply+
    是一种简单的列表求和方法

我将尝试一下你问题的HtDP部分,主要基于本书的第二部分——任意大数据。关于如何构造这样一个程序的设计,包括函数模板,有一些指导方针

首先,请注意您的代码:

  • 函数签名:(i)涉及整数,不能将其假定为sentinel值,并且大多数情况下,不适用于
    AVERAGE
    (ii)涉及ListOfNumber,不包括其数据定义。数据定义将决定函数模板
  • 关于不变量(我称之为“假设”,因为代码中没有维护它们的规定):它们真的有必要吗?
    guard
    是否必须是
    lon
    的成员?
    lon
    是否必须启动或至少有一个非负元素?
    lon
    是否必须为非空?这些答案也会影响数据定义
考虑:

;; ListOfNumber is one of:            VS           ;; ListOfNumber is one of:
;;  - empty                                        ;;  - (cons Number empty)
;;  - (cons Number ListOfNumber)                   ;;  - (cons Number ListOfNumber)
右边的数据定义意味着可以返回
empty
的函数,如本地
numbers-before-n
,不能声明它返回ListOfNumber

因此:

;;高级学生语言
;; 数据定义
;; ListOfNumber是以下各项之一:
;;  - 空的
;;  - (cons编号列表编号)
;; 解释作为降雨输入的数字列表
;; 使用ListOfNumber的函数的模板
(定义(lon的乐趣)
(cond[(空?lon)(…)]
[其他
(…(第一时间)。。。
…(龙的乐趣(休息龙))…)
;; 使用的模板规则:
;;  - 其中1例:2例
;;  - 原子:空
;;  - 复合:(cons编号列表OFNUMBER)
;;  - 自引用:(rest lon)是编号列表
;; 如果将保护值定义为常量,则模板将是上面的模板。
;; 但我会按照你的方式把它作为一个参数。
;; 作为输入的保护是一个数字,因此是原子的,因此模板成为
(定义(为lon-n提供乐趣)
(cond[(空?lon)(…n)]
[其他
(…n(第一个lon)。。。
…(为lon-n(rest-lon)n)找乐子)
;; 我们也可以返回零,如果:
;;   输入为空或在保护前没有非负值
;; 但如果我们决定根据输入情况区分降雨产生的影响:
;; Rainfall结果是以下结果之一:
;;  - “空输入”
;;  - “没有价值”
;;  - 编号[0,+inf.0)
解释
“空输入”表示输入没有值
“无值”表示输入在保护值之前没有非负值
;;-数字[0,+inf.0)表示输入在保护之前有有效值及其平均值
;为完整起见,函数的模板RainfallResult作为模板:
(定义(rr的乐趣)
(cond[(和(字符串?rr)(字符串=?rr“空输入”)(…)]
[(和(字符串?rr)(字符串=?rr“无值”)(…)]
[(和(数字?rr)(>=rr 0))(…rr)])
;使用的模板规则:
三个案例中的一个
-原子不同:“空输入”
-原子不同:“无值”
-原子非不同:数字[0,+inf.0)
功能
;降雨量(代码的列表平均值)
;;列表编号->Rainfall结果
;;生成保护值之前输入中出现的非负数的平均值
(选中expect(降雨空-99)“空输入”);lon为空
(选中expect(雨量(列表-99)-99)“无值”);lon仅包括防护罩
(检查expect(降雨(列表-99 1 2.5 3)-99)“无值”);lon从防护罩开始
(选中expect(降雨(列表-1-2.5-992)-99)“无值”);仅为负数值,是
(检查期望值(降雨量(列表-1.3-2.9-0.2)-99)“无值”);仅为负数值,无防护装置
(检查预计降雨量(列表1.5-0.9 2.5)-99)(/(+1.5 2.5)2));无防护装置
(检查期望值(降雨量(列表0-993)-99)(/(+0 0)4));防护负
(检查预计降雨量(列表1-10.12.5993)99)(/(+10.12.5)3));防护位置
(检查预计降雨量(列表1-10.12.599.53)99.5)(/(+10.12.5)3));防护浮子
;(定义(保护)0);存根
当我们意识到降雨是由子任务组成的,
;我们可以编写合成并将合成函数添加到WishList,
;(并且考虑语言是否提供了这样的功能)
;;使用带有n的ListOfNumber中的模板
(定义(防护罩)
(cond[(空?lon)“空输入”]
[其他
(平均lon(保护lon-guard之前的nn nums))))
愿望清单
平均长度(完成)
;;列表编号->Rainfall结果
;产生lon的平均值;如果lo
;; Advanced Student Language

;; Data Definitions

;; ListOfNumber is one of:
;;  - empty
;;  - (cons Number ListOfNumber)
;; interpret. a list of numbers as the input to Rainfall

;; Template for a function consuming ListOfNumber
(define (fun-for-lon lon)
  (cond [(empty? lon) (...)]
        [else
         (... (first lon) ...
          ... (fun-for-lon (rest lon)) ...)]))
;; Template rules used:
;;  - one of: 2 cases
;;  - atomic distinct: empty
;;  - compound: (cons Number ListOfNumber)
;;  - self-reference: (rest lon) is ListOfNumber

;; If the guard value was defined as a constant, the template would be the one above.
;; but i'll follow your way of it being a parameter.
;; the guard as input is a Number, thus Atomic, so the template becomes
(define (fun-for-lon-n lon n)
  (cond [(empty? lon) (... n)]
        [else
         (... n (first lon) ...
          ... (fun-for-lon-n (rest lon) n) ...)]))

;; We could just return zero also if:
;;   input is empty or without non-negative values before guard
;; But IF we decide to distinguish what Rainfall produces depending on input cases:

;; RainfallResult is one of:
;;  - "empty input"
;;  - "no values"
;;  - Number[0, +inf.0)
;; interpret.
;;  - "empty input"     means the input had no values
;;  - "no values"       means input had no non-negative values before guard value
;;  - Number[0, +inf.0) means the input had valid values before guard and their average
;; for completeness, template for function consuming RainfallResult has as template:
(define (fun-for-rr rr)
  (cond [(and (string? rr) (string=? rr "empty input")) (...)]
        [(and (string? rr) (string=? rr "no values")) (...)]
        [(and (number? rr) (>= rr 0)) (... rr)]))
;; Template rules used:
;;  - one of: 3 cases
;;  - atomic distinct: "empty input"
;;  - atomic distinct: "no values"
;;  - atomic non-distinct: Number[0, +inf.0)

;; Functions

;; rainfall (the list-average of your code)
;; ListOfNumber Number -> RainfallResult
;; produce the average of non-negative numbers that occur in the input before guard value
(check-expect (rainfall empty                 -99) "empty input")    ;lon is empty
(check-expect (rainfall (list -99)            -99) "no values") ;lon only includes guard
(check-expect (rainfall (list -99 1 2.5 3)    -99) "no values") ;lon starts with guard
(check-expect (rainfall (list -1 -2.5 -99 2)  -99) "no values") ;only neg. nums,yes guard
(check-expect (rainfall (list -1.3 -2.9 -0.2) -99) "no values") ;only neg. nums, no guard
(check-expect (rainfall (list 1.5 -0.9 2.5)         -99) (/ (+ 1.5 2.5)   2)) ;no guard
(check-expect (rainfall (list 0 0 0 0 -99 3)        -99) (/ (+ 0 0 0 0)   4)) ;guard neg
(check-expect (rainfall (list 1 -1 0.1 2.5 99 3)     99) (/ (+ 1 0.1 2.5) 3)) ;guard pos
(check-expect (rainfall (list 1 -1 0.1 2.5 99.5 3) 99.5) (/ (+ 1 0.1 2.5) 3)) ;guard float

;(define (rainfall lon guard) 0)    ;stub
;; When we realize that rainfall is composed of subtasks,
;; we can write the composition and add the compositing functions to WishList,
;; (and consider if the language provides such a function)
;; used template from ListOfNumber with n
(define (rainfall lon guard)
  (cond [(empty? lon) "empty input"]
        [else
         (average-lon (nn-nums-before-guard lon guard))]))


;; Wish List

;; average-lon  (done)
;; ListOfNumber -> RainfallResult
;; produce the average of lon; if lon is empty, return "no values"
;; Assume that lon is a list of non-negative numbers
(check-expect (average-lon empty)          "no values")
(check-expect (average-lon (list 0))       (/ 0 1))
(check-expect (average-lon (list 1 3 5 0)) (/ (+ 1 3 5 0) 4))
;(define (average-lon lon) 0)   ;stub
;; used template from ListOfNumber
#;
(define (average-lon lon)
  (cond [(empty? lon) "no values"]
        [else
         (/ (sum lon) (how-many lon))]))
;; to save some space:
;; the (how-many lon), well, is (length lon)
(define (average-lon lon)
  (cond [(empty? lon) "no values"]
        [else
         (/ (sum lon) (length lon))]))
;; for the (sum lon) we could also:
;; either (foldr + 0 lon) like in your code
;; or (apply + lon) like in the accepted answer
;; but for now let's add it to the wish list and implement it

;; nn-nums-before-guard  (done)
;; ListOfNumber Number -> ListOfNumber
;; produce a list of non-negative nums occuring in lon before n
(check-expect (nn-nums-before-guard empty          -99) empty)
(check-expect (nn-nums-before-guard (list -99 1 2) -99) empty)
(check-expect (nn-nums-before-guard (list -1 -2)   -99) empty)
(check-expect (nn-nums-before-guard (list 1 2 3)        -99) (list 1 2 3))
(check-expect (nn-nums-before-guard (list 1 2 3 -99 1)  -99) (list 1 2 3))
(check-expect (nn-nums-before-guard (list 1 -2 3 -99 1) -99) (list 1 3))
;(define (nn-nums-before-guard lon n) empty)       ;stub
;; used template from ListOfNumber with n
(define (nn-nums-before-guard lon guard)
  (cond [(empty? lon) empty]
        [else
         (cond
           [(= (first lon) guard) empty]
           [(< (first lon) 0) (nn-nums-before-guard (rest lon) guard)]
           [else (cons (first lon)
                       (nn-nums-before-guard (rest lon) guard))])]))


;; sum  (done)
;; ListOfNumber -> Number
;; produce the sum of numbers in lon
(check-expect (sum empty) 0)
(check-expect (sum (list 1)) 1)
(check-expect (sum (list 1 -1 2 -2 3 -3)) 0)
;(define (sum lon) 0)  ;stub
;; used template from ListOfNumber
(define (sum lon)
  (cond [(empty? lon) 0]
        [else
         (+ (first lon)
            (sum (rest lon)))]))