Stream 无法理解/可视化SICP流汉明数字程序

Stream 无法理解/可视化SICP流汉明数字程序,stream,scheme,lazy-evaluation,sicp,hamming-numbers,Stream,Scheme,Lazy Evaluation,Sicp,Hamming Numbers,我基本上被困在了SICP的练习3.56中。问题是这样的: 练习3.56。一个著名的问题,首先由R.Hamming提出,是按升序不重复地枚举除2、3或5以外没有素因子的所有正整数。一个显而易见的方法是依次测试每个整数,看看它是否有除2、3和5以外的其他因子。但这是非常低效的,因为随着整数变大,满足要求的整数越来越少。作为替代方案,让我们调用所需的数字流S,并注意以下事实 S以1开头。 (比例流S2)的元素也是S的元素 (比例流S3)和(比例流5S)也是如此 这些都是S 现在我们所要做的就是

我基本上被困在了SICP的练习3.56中。问题是这样的:

练习3.56。一个著名的问题,首先由R.Hamming提出,是按升序不重复地枚举除2、3或5以外没有素因子的所有正整数。一个显而易见的方法是依次测试每个整数,看看它是否有除2、3和5以外的其他因子。但这是非常低效的,因为随着整数变大,满足要求的整数越来越少。作为替代方案,让我们调用所需的数字流S,并注意以下事实

  • S以1开头。
    • (比例流S2)的元素也是S的元素
    • (比例流S3)和(比例流5S)也是如此
    • 这些都是S
现在我们所要做的就是合并这些来源的元素。为此,我们定义了一个过程合并,将两个有序流合并为一个有序结果流,消除重复:

(define (merge s1 s2)
   (cond ((stream-null? s1) s2)
         ((stream-null? s2) s1)
         (else
          (let ((s1car (stream-car s1))
                (s2car (stream-car s2)))
            (cond ((< s1car s2car)
                   (cons-stream s1car (merge (stream-cdr s1) s2)))
                  ((> s1car s2car)
                   (cons-stream s2car (merge s1 (stream-cdr s2))))
                  (else
                   (cons-stream s1car
                                (merge (stream-cdr s1)
                                       (stream-cdr s2)))))))))

提前感谢。

这是我将其可视化的最佳尝试。但我确实很挣扎,感觉就像一条三头蛇在吃自己的尾巴

If we say the values of the stream S are s0, s1, s2, ..., then 
initially we only know the first value, s0.

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    ?    ?    ?    ?    ?    ?    ?    ?    ?    ?  

But we do know the three scale-streams will be producing multiples of
these values, on demand:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    ?    ?    ?    ?    ?    ?    ?    ?    ?    ?  

    scale-2:   2*1  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:   3*1  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:   5*1  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


Merge will initially select the lowest of the numbers at the heads of
these three streams, forcing their calculation in the process:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    ?    ?    ?    ?    ?    ?    ?    ?    ?    ?  

    scale-2:  [2]  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:   3   3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:   5   5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


 So s1 will now have the value 2:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1   [2]   ?    ?    ?    ?    ?    ?    ?    ?    ?  

    scale-2:        2*2  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:   3    3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:   5    5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


Merge will now select 3 as the minimum of 4, 3, and 5:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    ?    ?    ?    ?    ?    ?    ?    ?    ?  

    scale-2:        4    2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:  [3]   3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:   5    5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


and will put it into the next slot in the result stream S, s2:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2   [3]   ?    ?    ?    ?    ?    ?    ?    ?  

    scale-2:        4    2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:        3*2  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:   5    5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


Scale-2's head is selected again:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3   [4]   ?    ?    ?    ?    ?    ?    ?  

    scale-2:             2*3  2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:        6    3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:   5    5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


And then 5 is selected from scale-5 and placed in the result:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4   [5]   ?    ?    ?    ?    ?    ?  

    scale-2:             6    2*?  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:        6    3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:        5*2  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


Two streams have 6 at their head, both are consumed but only one 6 
is placed in the result:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4    5   [6]   ?    ?    ?    ?    ?  

    scale-2:                  2*4  2*?  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:             3*3  3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:        10   5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


And a few more iterations:

               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4    5    6   [8]   ?    ?    ?    ?  

    scale-2:                       2*5  2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:             9    3*?  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:        10   5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4    5    6    8   [9]   ?    ?    ?  

    scale-2:                       10   2*?  2*?  2*?  2*?  2*?  2*?
    scale-3:                  3*4  3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:        10   5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    _________________________________________________________________


               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4    5    6    8    9   [10]  ?    ?  

    scale-2:                            2*6  2*?  2*?  2*?  2*?  2*?
    scale-3:                  12   3*?  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:             5*3  5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________


               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4    5    6    8    9    10  [12]  ?  

    scale-2:                                 2*8  2*?  2*?  2*?  2*?
    scale-3:                       3*5  3*?  3*?  3*?  3*?  3*?  3*?
    scale-5:             15   5*?  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    _________________________________________________________________


               s0   s1   s2   s3   s4   s5   s6   s7   s8   s9   s10
           S = 1    2    3    4    5    6    8    9    10   12  [15]

    scale-2:                                 16   2*?  2*?  2*?  2*?
    scale-3:                            3*6  3*?  3*?  3*?  3*?  3*?
    scale-5:                  5*4  5*?  5*?  5*?  5*?  5*?  5*?  5*?
    ________________________________________________________________

因此,也许它更像是一条一头从三条尾巴交替咬伤的蛇。

作为一个恰当的命名,
merge
不应该删除重复项,因为它的名字表明它是
mergesort
的一部分,应该保留它们
Union
是此类操作的更好名称,它通过增加唯一数列表来表示集合(此处),它应该通过删除只能来自两个参数的重复项来保留该约束

回到问题本身,让我们把它象征性地写为

S235={1}∪ 2*S235∪ 3*S235∪ 5*S235

(等等,什么?)我们甚至不会试图确定这些
执行他们的工作,甚至不按哪个顺序。甚至有多少个术语:

S23={1}∪ 2*S23∪ 3*S23

甚至

S2={1}∪ 2*S2

现在这看起来很简单。我们甚至可以在这里简单地实现
A
B
的联合,首先取
A
的所有元素,然后——取
B
。在这里,它可以很好地工作,因为在这个
的左输入:

 {1} ----∪-->--->--S₂--.--->S₂
        /               \        
        \______*2_______/        
          ---<----<---         
两个
S₂,是相同的,因为我们在拆分点从中抽取的任何东西都不会影响它

这不是很有趣吗

那么,我们如何将
3的倍数添加到它?一种方法是

S23=S2∪ 3*S23

为什么要增加订单?怎么用?为什么,这是
!你好,另一个发现的需求。无论从哪一侧进入,它都必须先产生较小的元素,然后再产生较大的元素

如果两者相等,该怎么办?在这项计划中,我们是否需要关注这个问题?这会在这里发生吗

不可能。因此我们可以实现
在这里作为一个
合并
,而不是作为一个
联合
(但是记住第一个发现的需求!——它仍然有效吗?需要吗?添加新的案例)
Merge
应该比
union
更有效,因为它不涉及相等的情况

对于5的倍数呢?我们继续,作为

S235=S23∪ 5*S235

{1}----∪-->--->--s₂--.---s₂----∪-->--->--s₂₃--.---s₂₃----∪-->--->--s₂₃₅--.--->s₂₃₅
/               \       /                \         /                 \ 
\______*2_______/       \______*3________/         \_______*5________/ 

---现在您已经看到了答案,您是否尝试过在计算这些表达式之前,Scheme如何展开这些表达式?我认为这可能有助于您理解这里发生了什么(使用书中给出的cons stream的等效定义,在手动扩展时使用delay)。我建议您至少完成流的扩展,直到达到6(流中的最低数字,是两个不同因素的倍数)。尝试使用显式对象对其进行编码,表示为具有可变状态的闭包,该闭包显式地从供应商处提取输入以生成输出(作为生成器的一个可能模型)。您将在这里发现许多隐藏的东西、可能性和选择(参见Python的
tee
函数及其复杂性)。然后,切换回流,您将能够了解流是如何自动完成的(和/或更好),甚至可以看到可能的流实现中的不同选择。非常感谢。很高兴知道我不是唯一一个觉得这很难想象的人。很好的回答,谢谢你,阿加尼特不是一条蛇。有两个。每一个在这里被称为
merge
merge
在小范围内工作:它不知道其输入来自之前输出的内容。它只需查看两个输入头,比较它们,然后抽取最小的一个。两个合并被安排在一个(最短的)树中,因此较低的合并的输出作为较高的合并的正确输入。你认为如果我在我的答案中添加了完全遵循本书代码的图表,值得吗?我们必须在这里努力“在小范围内工作”,像对待递归一样“放手并有信心”:递归的全部目的不是试图看到整个机制工作,而是专注于较小的部分,确保不变量被保留,这样就可以将较小的部分合成为一个较大的部分
 {1} ----∪-->--->--S₂--.--->S₂
        /               \        
        \______*2_______/        
          ---<----<---         
S2 = {1} ∪ 2*{1} ∪ 2*2*{1}                ;; == {1, 2, 4, 8, 16, 32, ...}
                 ∪ 2*2*2*{1}
                 ∪ 2*2*2*2*{1}
                 ∪ ..........
 {1} ----∪-->--->--S₂--.---S₂----∪-->--->--S₂₃--.--->S₂₃
        /               \       /                \        
        \______*2_______/       \______*3________/        
          ---<----<---            ---<----<---         
S23 = S2 ∪ 3*S2 ∪ 3*3*S2                   ;; = S2 ∪ 3*( S2 ∪ 3*S2 
                ∪ 3*3*3*S2                 ;;               ∪ 3*3*S2 
                ∪ 3*3*3*3*S2               ;;               ∪ 3*3*3*S2 
                ∪ ..........               ;;               ∪ ........ )   !!
 {1} ----∪-->--->--S₂--.---S₂----∪-->--->--S₂₃--.---S₂₃----∪-->--->--S₂₃₅--.--->S₂₃₅
        /               \       /                \         /                 \ 
        \______*2_______/       \______*3________/         \_______*5________/ 
          ---<----<---            ---<----<---                ---<----<---     
                                  1 --->---\
                                             cons-stream ->-- S ---.---> S
    /----->---->--- *2 --->---\            /                       |
   /                            union ->--/                        /
  .-->-- *3 -->--\            /                                   /
  |                union ->--/                                   /
  .-->-- *5 -->--/                                              /
  \                                                            /
   \__________<__________<__________<_________<_______________/