Functional programming 在某些前哨值(如-999)之前查找输入中出现的非负数的平均值
在论文中提到了一个实验的编程任务 任务:“大致上,降雨要求非负数的平均值 在某些前哨值(如-999)之前的输入中出现” 我决定尝试使用高级学生语言(ASL)以HtDP方式解决这个问题 谈到有效的解决方案,我想知道是否有办法使它更优雅。 此外,还不清楚如何根据HtDP手册中介绍的设计方法对这样的函数进行模板化 以下是我的解决方案: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
;; 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+
- 函数签名:(i)涉及整数,不能将其假定为sentinel值,并且大多数情况下,不适用于
(ii)涉及ListOfNumber,不包括其数据定义。数据定义将决定函数模板AVERAGE
- 关于不变量(我称之为“假设”,因为代码中没有维护它们的规定):它们真的有必要吗?
是否必须是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)))]))