(方案)取一个列表并返回新的列表,包括正数,负数和零的数量 [英] (Scheme) Take a list and return new list with count of positive, negative, and zeros

查看:130
本文介绍了(方案)取一个列表并返回新的列表,包括正数,负数和零的数量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图接受一个列表,让它计数正数,负数和零,并返回一个新列表。

我在调试时唯一注意到的是列表正在迭代,但它没有使用任何条件。所以它成功地递归地调用自己,然后它只是一旦空它就出错。

 (define(mydisplay value)
(显示值)
(换行符)
#t


(define neg 0)
(define z 0)
(define (0)$($ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ (1))
(ZERO?(car(lst))(+ 1 z))
(else(+ 1 pos))


posneg(cdr lst))

(mydisplay(posneg'(1 2 3 4 2 0 -2 3 23 -3)))
(mydisplay(posneg'(-1 2 - ($)
(mydisplay(posneg'()))

$ b $好的,我最喜欢的技巧是一厢情愿的想法,我从Gerald Jay Sussman和Hal Abelson那里了解到 a href =http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/ = nofo llow>计算机程序结构和解释(SICP)课程。特别是,视频讲座2B。化合物数据将会对你有利。



让我们先假装( wishing ),让这个数据容器可用我们拥有 3 值:一个用于 p ositives,一个用于 n egatives,另一个用于 z eros。我们称它为 pnz



创建其中之一的方法很简单

 ;;构造一个pnz,它有1个正数,4个负数和2个零
(define x(make-pnz 1 4 2))

选择肯定值

 (positives x); => 1 

选择负值

 (negatives x); => 4 

选择零值

 (zeros x); => 2 

暂时忘记这些程序不存在( yet )。相反,只是希望他们做了,并开始编写过程来解决您的问题。



我们将从一些伪代码开始

 ;;伪代码
如果xs为null,则定义count-pnz xs
?如果(汽车xs)为正值,则返回(make-pnz pnz)
,如果(汽车xs)为负值,则将正数更新一个
;如果(汽车xs)为负值,则更新负值计数
xs)为零,将零计数更新一个
return count-pnz(cdr xs)



<好的,这实际上很简单。那么,有一点小问题。注意我在说更新计数?那么我们需要在某个地方存储计数,因为该过程正在迭代。让我们稍微调整一下伪代码,这次包括一个 pnz 参数来跟踪我们当前的计数。

 ;伪代码v2 
define count-pnz xs pnz =(0 0 0)
如果xs为null?如果(car xs)为正,则返回(make-pnz pnz)
nextpnz =(make-pnz p + 1 nz)
如果(car xs)为负,则nextpnz =(make-pnz p n +1 z)
if(car xs)is zero,nextpnz =(make-pnz pn z + 1)
return count-pnz(cdr xs)nextpnz

现在这个过程对我来说很有意义。在最简单的情况下, xs 是一个空列表,它将简单地返回一个 pnz (0 0 0)。如果 xs 具有任意数量的值,它将一个接一个遍历列表,并在 pnz <

将此转换为方案非常简单

$ C>;一厢情愿的想法
;我们将在后面定义make-pnz,positives,negatives和zero
(define(count-pnz xs(pnz(make-pnz 0 0 0)))
(let((p(positives pnz) )
(n(negatives pnz))
(z(zeros pnz))]
(cond [(null?xs)pnz]
[(>(car xs) 0)(count-pnz(cdr xs)(make-pnz(+ 1p)nz))]
[(<(car xs)0)(count-pnz(cdr xs)(make-pnz p (+ 1 n)z))]
[(=(car xs)0)(count-pnz(cdr xs)(make-pnz pn(+ 1 z)))])))

你会发现我在这里使用了 let 来制作它更容易引用个人 p n z 值的当前迭代。这样,当我们检测到正数,负数或零时,我们可以通过简单地执行(+ 1 p)( + 1 n)(+ 1 z)。不要增加的值可以简单地在未触及的情况下传递。



我们正在变得非常接近。我们的过程是合乎逻辑的,但我们需要定义 make-pnz positives / code>和 zeros ,然后才能工作。顺便说一下,通过创建构造函数选择器来定义数据对象以将表示与表示隔离的方法被称为数据抽象。如果您有兴趣,您可以在我关联的视频中了解到更多相关信息。



因此,我们需要履行合同

 ; PNZ合同
; pnz *必须*表现得像这样
(positives(make-pnz pnz))⇒p
(negatives(make-pnz pnz))⇒n
(零(make-pnz pnz)) ⇒z

让我们来实现它!

 ;构造函数
(define(make-pnz p n z)
(list p n z))

;肯定选择
(define(positives pnz)
(car pnz))

;负数选择器
(定义(负数pnz)
(cadr pnz))

;零选择符
(define(zeros pnz)
(caddr pnz))

Pff,这很容易!使用列表 car cadr caddr 使我们的工作变得简单,并且很容易理解 pnz 的行为。



 (define answer(count-pnz'(-1 2) -3 4 2 0 -2 3 -23 -3 0 0)))
(正答案); => 4
(否定答案); => 5
(零答案); => 3

现在就有了它。数据抽象是一种非常强大的概念。

数据抽象是一个非常强大的概念。您可能会想,为什么我们不在 count-pnz 过程中使用 list 所有这个构造函数/选择器仪式?答案可能并不明显,但对于我来说这篇文章有点太过分了。相反,我诚挚地希望你能看看我连接的学习资源,因为我确信他们会对你有很大的好处。







@DavinTryon说@ naomik的答案可以在列表之外定义(甚至只是函数)。

/ blockquote>

是的,这是完全正确的。让我们看看 make-pnz positives 底片和以不同的方式实施。请记住,仍然必须履行合同才能使此实施有效。

;构造函数
(define(make-pnz p n z)
(λ(f)(f p n z)))

;选择器
(define(positives pnz)
(pnz(λ(pnz)p)))

(define(negatives pnz)
(pnz(λ(pnz )n)))

(define(零pnz)
(pnz(λ(pnz)z)))

很酷。这证明了数据抽象的美妙之处。我们完全可以重新实现 make-pnz positives 底片 zeros 以不同的方式,但由于我们仍然完成了原始合同,我们的 count-pnz 功能根本不需要改变。


I am attempting to accept a list, have it count the positive, negative, and zeros, and return a new list.

The only thing I notice as I'm debugging is that the list is iterating through, but it is not utilizing any of the conditionals. So its successfully recursively calling itself, and then it just errors once its empty.

(define (mydisplay value)
(display value)
(newline)
#t
)

(define neg 0)
(define z 0)
(define pos 0)

  (define (posneg lst)

  (cond 
    ((NULL? lst))
    (NEGATIVE? (car lst) (+ 1 neg))
    (ZERO? (car (lst)) (+ 1 z))
    (else (+ 1 pos))
   )

  (posneg (cdr lst))
)
(mydisplay (posneg '(1 2 3 4 2 0 -2 3 23 -3)))
(mydisplay (posneg '(-1 2 -3 4 2 0 -2 3 -23 -3 0 0)))
(mydisplay (posneg '()))

解决方案

OK, my favorite technique to apply here is wishful thinking as I learned it from Gerald Jay Sussman and Hal Abelson in the Structure and Interpretation of Computer Programs (SICP) course. Particularly, video lecture 2B. Compound Data will be of interest to you.

Let's start by just pretending (wishing) with have this data container available to us that holds 3 values: one for positives, one for negatives, and one for zeros. We'll call it pnz.

The way to create one of these is simple

; construct a pnz that has 1 positive, 4 negatives, and 2 zeros
(define x (make-pnz 1 4 2))

To select the positives value

(positives x) ;=> 1

To select a negatives value

(negatives x) ;=> 4

To select the zeros value

(zeros x) ;=> 2

Forget for a moment that these procedures don't exist (yet). Instead, just wish that they did and begin writing the procedure to solve your problem.

We'll start with some pseudocode

; pseudocode
define count-pnz xs
  if xs is null? return (make-pnz p n z)
  if (car xs) is positive, update the positive count by one
  if (car xs) is negative, update the negative count by one
  if (car xs) is zero, update the zero count by one
  return count-pnz (cdr xs)

OK, that's pretty straight forward actually. Well, with one little gotcha. Notice I'm saying "update the count by one"? Well we need somewhere to store that count as the procedure is iterating. Let's make a slight adjustment to the pseudocode, this time including a pnz parameter to keep track of our current count

; pseudocode v2
define count-pnz xs pnz=(0 0 0)
  if xs is null? return (make-pnz p n z)
  if (car xs) is positive, nextpnz = (make-pnz p+1 n z)
  if (car xs) is negative, nextpnz = (make-pnz p n+1 z)
  if (car xs) is zero, nextpnz = (make-pnz p n z+1)
  return count-pnz (cdr xs) nextpnz

Now this procedure makes sense to me. In the simplest case where xs is an empty list, it will simply return a pnz of (0 0 0). If xs has any number of values, it will iterate through the list, one-by-one, and increment the corresponding value in the pnz container.

Translating this into scheme is a breeze

; wishful thinking
; we will define make-pnz, positives, negatives, and zeros later
(define (count-pnz xs (pnz (make-pnz 0 0 0)))
  (let [(p (positives pnz))
        (n (negatives pnz))
        (z (zeros pnz))]
    (cond [(null? xs) pnz]
          [(> (car xs) 0) (count-pnz (cdr xs) (make-pnz (+ 1 p) n z))]
          [(< (car xs) 0) (count-pnz (cdr xs) (make-pnz p (+ 1 n) z))]
          [(= (car xs) 0) (count-pnz (cdr xs) (make-pnz p n (+ 1 z)))])))

You'll notice I used a let here to make it easier to reference the individual p, n, z values of the current iteration. That way, when we detect a positive, negative, or zero, we can easily increment the appropriate value by simply doing (+ 1 p), (+ 1 n), or (+ 1 z) accordingly. Values that are not meant to be incremented can simply be passed on untouched.

We're getting extremely close. Our procedure make logical sense but we need to define make-pnz, positives, negatives, and zeros before it can work. By the way, this methodology of defining data objects by creating constructors and selectors to isolate use from representation is called data abstraction. You'll learn more about that in the video I linked, if you're interested.

So here's the contract that we need to fulfill

; PNZ CONTRACT
; pnz *must* behave like this
(positives (make-pnz p n z)) ⇒ p
(negatives (make-pnz p n z)) ⇒ n
(zeros     (make-pnz p n z)) ⇒ z

Let's implement it !

; constructor
(define (make-pnz p n z)
  (list p n z))

; positives selector
(define (positives pnz)
  (car pnz))

; negatives selector
(define (negatives pnz)
  (cadr pnz))

; zeros selector
(define (zeros pnz)
  (caddr pnz))

Pff, well that was easy as can be ! Using a list, car, cadr, and caddr made our job simple and it's easy to understand how pnz behaves.

Without further ado, let's see this thing in action now

(define answer (count-pnz '(-1 2 -3 4 2 0 -2 3 -23 -3 0 0)))
(positives answer) ; => 4
(negatives answer) ; => 5
(zeros answer)     ; => 3

And there you have it. Wishful thinking and a dash of data abstraction to the rescue.

Data abstraction is a very powerful concept. You might be thinking, "Why didn't we just use list in the count-pnz procedure instead of all of this constructor/selector ceremony?" The answer may not be readily apparent, but it is a bit too much for me to get into with this post. Instead, I sincerely do hope you check out the learning resources I linked as I'm certain they will be of great benefit to you.


@DavinTryon says "@naomik's answer could be defined in something other than a list (even just functions)."

Yep, that's totally true. Let's see make-pnz, positives, negatives, and zero implemented in a different way. Remember, the contract still has to be fulfilled in order for this implementation to be considered valid.

; constructor
(define (make-pnz p n z)
  (λ (f) (f p n z)))

; selectors
(define (positives pnz)
  (pnz (λ (p n z) p)))

(define (negatives pnz)
  (pnz (λ (p n z) n)))

(define (zeros pnz)
  (pnz (λ (p n z) z)))

Pretty cool. This demonstrates the beauty of data abstraction. We were able to completely re-implement make-pnz, positives, negatives, and zeros in a different way, but because we still fulfilled the original contract, our count-pnz function does not need to change at all.

这篇关于(方案)取一个列表并返回新的列表,包括正数,负数和零的数量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆