【问题标题】:return a list with the results of the execution of a for loop返回一个包含 for 循环执行结果的列表
【发布时间】:2023-03-07 00:33:01
【问题描述】:

我刚开始学习球拍,我需要循环一个带有索引的列表。

现在,我正在使用for

#lang racket
(for ([index (in-range 0 (length list1))])
  (if (number? (list-ref list1 index))
      (function-numeric list1 index list2)
      (function-no-numeric list1 index list3)))

function-numericfunction-no-numeric 返回一个列表,我需要创建一个列表,这些列表由这些函数返回。但我不知道该怎么做。

也许map 会更容易,但我不这样做。

如何使用 for 循环返回列表列表?

【问题讨论】:

  • 有时需要用不熟悉的语言进行编程,然后将其弯曲成熟悉的形状。遗留代码、平台细节等。但你暗示你想学习球拍。您无法通过尝试将其转换为 C 派生来做到这一点。如果你坚持,你可以这样编程,有时出于性能原因这是必要的,但你不会学习它。

标签: functional-programming racket


【解决方案1】:

如果你刚刚开始学习 Racket,我真的不推荐 forfor/list。你应该先了解基本的:什么是列表。

列表(也称为链表)是:

  • empty;或
  • 一个元素的cons 与另一个列表

例如,

  • empty 是一个空列表。
  • (cons 9 empty) 是一个包含一个元素的列表:9
  • (cons 3 (cons 9 empty)) 是一个包含两个元素的列表:39

给定一个列表,以下是您可以执行的基本操作(cons 除外)。

  • (empty? lst):检查lst是否为empty。例如,(empty? empty) 的计算结果为 #t,但 (empty? (cons 1 empty)) 的计算结果为 #f
  • (first lst):如果lstcons,则返回lst 的第一个元素。如果lstempty,则会出错。例如,(first (cons 2 (cons 1 empty))) 的计算结果为 2,但 (first empty) 会导致错误。
  • (rest lst):如果lstcons,则返回lst 的其余部分。如果lstempty,则会出错。例如,(rest (cons 2 (cons 1 empty))) 的计算结果为 (cons 1 empty),但 (first empty) 会导致错误。

结合使用firstrest,您可以访问任何元素。如果您有一个列表 (cons 5 (cons 4 (cons 3 (cons 2 empty)))) 并且您想要访问第二个元素(应该是 4),您将计算:

  • (rest lst) 这会给你(cons 4 (cons 3 (cons 2 empty)))
  • (first (rest lst)) 会给你4

list 只是简单的缩写,可以轻松构建列表

  • (list)empty 的缩写。
  • (list 9)(cons 9 empty) 的缩写
  • (list 1 2 3)(cons 1 (cons 2 (cons 3 empty))) 的缩写。

其他列表操作仅使用上述原始操作来实现。比如list-ref的实现如下:

(define (my-list-ref xs i)
  (cond
    ;; a list is either empty
    [(empty? xs) (error 'out-of-bound)]
    ;; or a cons, where it's safe to use first and rest
    [(= i 0) (first xs)]
    [else (my-list-ref (rest xs) (- i 1))]))

;; (my-list-ref (list) 0) => out-of-bound error
;; (my-list-ref (list 4 5 6) 0) => 4
;; (my-list-ref (list 4 5 6) 1) => 5
;; (my-list-ref (list 4 5 6) 2) => 6

正如 Óscar López 所提到的,虽然列表看起来类似于数组,但考虑它们的方式却大不相同。使用list-ref 需要O(i),如果您想一次获取第i 个元素,这很好,但它不是访问所有元素(甚至很多元素)的正确方法。相反,只需一次访问所有这些。例如,如果我有一个列表 (list 2 3 4),并且我想获得另一个列表,并将 10 添加到原始列表的每个元素中,我会写:

(define (add10-all xs)
  (cond
    ;; a list is either empty, where (add10-all empty) should return empty
    [(empty? xs) empty]
    ;; or a cons, where we want to to add 10 to the first element, 
    ;; recur on the rest, and create a resulting cons
    [else (cons (+ 10 (first xs)) (add10-all (rest xs)))]))

;; (add10-all (list)) => (list)
;; (add10-all (list 2 3 4)) => (list 12 13 14)

在你的情况下,你可能想要:

;; assume function-numeric consumes an element from list1 and the whole list2
;; assume function-no-numeric consumes an element from list1 and the whole list3

(define (foo list1 list2 list3)
  (cond
    [(empty? list1) empty]
    [else
     (define e (first list1))
     (define out (if (number? e)
                     (function-numeric e list2)
                     (function-no-numeric e list3)))
     (cons out (foo (rest list1) list2 list3))]))

(foo list1 list2 list3)

;; assume function-numeric consumes an element from list1 and the corresponding element in list2
;; assume function-no-numeric consumes an element from list1 and the corresponding element in list3

(define (foo list1 list2 list3)
  (cond
    [(empty? list1) empty]
    [else
     (define e (first list1))
     (define out (if (number? e)
                     (function-numeric e (first list2))
                     (function-no-numeric e (first list3))))
     (cons out (foo (rest list1) (rest list2) (rest list3)))]))

(foo list1 list2 list3)

可能是你想做别的事,但结构应该和上面两段代码差不多。例如,如果你碰巧真的需要一个索引来计算function-numericfunction-no-numeric,你可以这样写:

;; assume function-numeric consumes (1) an element from list1 (2) the corresponding element in list2 (3) an index
;; assume function-no-numeric consumes (1) an element from list1 (2) the corresponding element in list3 (3) an index

(define (foo list1 list2 list3 i)
  (cond
    [(empty? list1) empty]
    [else
     (define e (first list1))
     (define out (if (number? e)
                     (function-numeric e (first list2) i)
                     (function-no-numeric e (first list3) i)))
     (cons out (foo (rest list1) (rest list2) (rest list3) (+ i 1)))]))

(foo list1 list2 list3 0)

【讨论】:

  • 我不知道firstrest 我一直使用carcdr。另外,我总是使用null? 而不是empty,或者只是对照'() 检查列表。
【解决方案2】:

对于初学者来说,使用索引是遍历列表的最坏方法,应该避免:每个list-ref 操作都是O(n),因为要到达元素i,您必须遍历它之前的所有i-1 元素,使整个遍历成为一个惊人的O(n^2) 操作。如果您绝对需要它们,则使用索引生成一个单独的列表。

为了将返回的数据收集到一个列表中,我们可以利用for/list对每次迭代的结果进行累加,甚至可以并行遍历多个元素序列。让我们把所有东西放在一起:

(for/list ([element list1]
           [index (in-range (length list1))])
  (if (number? element)
      (function-numeric list1 index list2)
      (function-no-numeric list1 index list3)))

我只是希望你不要在function-numericfunction-no-numeric 中使用list-ref 再次。也许有更好的方法来构建您的算法,以完全避免索引 - 不应该像使用数组一样使用列表!如果您的算法无法修改以避免索引,那么请考虑使用vector,它针对使用索引的快速项目检索进行了优化。

【讨论】:

  • 我尝试了你的建议:“使用索引生成一个单独的列表”,我使用了 map,但是 map 遍历第一个列表,然后是第二个。
  • 这听起来不对。你怎么称呼它的?如果你这样做:(map (lambda (e1 e2) do-something) lst1 lst2) 它会同时遍历两个列表。
  • 这就是我所做的:(define lst1 '(1 2 3 4))(map (lambda (e1 e2) (display e1)) lst1 (range 0 (length lst1)))。我得到:1234'(#<void> #<void> #<void> #<void>)。可能我还没明白发生了什么。
  • map 不是这样工作的,你使用的lambda 必须返回 一些东西。 display 只是在控制台中打印一个值,然后返回 #<void>。将display 调用替换为:(list e1 e2),您会发现它可以正常工作。
  • 是的,这比使用for/list 更便携,后者只能在 Racket 中使用。
猜你喜欢
  • 2019-02-08
  • 2018-06-05
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 2014-01-24
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多