scheme - LISP 中以索引作为参数之一的映射函数

标签 scheme lisp racket

LISP 语言(或特别是 Racket)中是否有任何内置函数可以像 map 一样工作,但将元素的索引作为参数之一传递给映射函数?

此类函数的示例是:

(define map-index (lambda (func list)
  (map func list (build-list (length list) (lambda (i) i)))))

;usage:
> (map-index cons '(a b c d))
;output:
'((a . 0) (b . 1) (c . 2) (d . 3))

显然这不是一个非常有效的实现,并且不支持多个列表作为参数,就像常规 map 一样。

最佳答案

<小时/>

Racket

<小时/>

您可以使用 Racket range 过程并映射结果来编写 map-index 的极其简单的版本:

(define (map-index-1 f xs)
  (map f xs (range (length xs))))

在某些情况下,您可能首先需要索引:

(define (map-index-2 f xs)
  (map f (range (length xs)) xs))

如果您希望能够在多个列表上使用map-index,您可以将列表参数传递给可选参数。此处,applymap 过程应用于由函数 f、输入列表和 范围 构造的列表> 列表:

(define (map-index-3 f . xs)
  (apply map (cons f
                   (append xs
                           (list (range (length (car xs))))))))

但是在映射多个列表时,将索引放在前面可能更有意义:

(define (map-index-4 f . xs)
  (apply map (cons f
                   (cons (range (length (car xs)))
                         xs))))
scratch.rkt> (map-index-1 cons '(a b c d))
'((a . 0) (b . 1) (c . 2) (d . 3))

scratch.rkt> (map-index-2 cons '(a b c d))
'((0 . a) (1 . b) (2 . c) (3 . d))

scratch.rkt> (map-index-3 list '(a b c d) '(one two three four) '(w x y z))
'((a one w 0) (b two x 1) (c three y 2) (d four z 3))

scratch.rkt> (map-index-4 list '(a b c d) '(one two three four) '(w x y z))
'((0 a one w) (1 b two x) (2 c three y) (3 d four z))
<小时/>

方案

<小时/>

标准方案没有内置的range过程,但编写一个简单版本很容易。这些解决方案适用于任何 R4RS、R5RS、R6RS 或 R7RS 方案实现。此版本的 range 的功能超出了当前应用程序的要求,它采用可以是正值或负值的 step 参数:

(define (range start stop step)
  (if (or (and (> step 0)
               (>= start stop))
          (and (<= step 0)
               (<= start stop)))
      '()
      (cons start (range (+ start step) stop step))))

定义了range过程后,可以在Scheme中使用与上述Racket解决方案相同的方法:

(define (map-index-5 f xs)
  (map f xs (range 0 (length xs) 1)))

(define (map-index-6 f xs)
  (map f (range 0 (length xs) 1) xs))

(define (map-index-7 f . xs)
  (apply map (cons f
                   (append xs
                           (list (range 0 (length (car xs)) 1))))))

(define (map-index-8 f . xs)
  (apply map (cons f
                   (cons (range 0 (length (car xs)) 1)
                         xs))))
> (map-index-5 cons '(a b c d))
((a . 0) (b . 1) (c . 2) (d . 3))

> (map-index-6 cons '(a b c d))
((0 . a) (1 . b) (2 . c) (3 . d))

> (map-index-7 list '(a b c d) '(one two three four) '(w x y z))
((a one w 0) (b two x 1) (c three y 2) (d four z 3))

> (map-index-8 list '(a b c d) '(one two three four) '(w x y z))
((0 a one w) (1 b two x) (2 c three y) (3 d four z))
<小时/>

标准方案的map-range过程

<小时/>

通过利用 range 函数的功能,可以将此方法扩展为使用可能不表示索引的更复杂范围内的数字。使用上面的 range 定义,此 map-range 过程仍然适用于 R4RS 到 R7RS 方案实现:

(define (map-range f start step . xs)
  (let ((stop (+ start (* step (length (car xs))))))
    (apply map (cons f
                     (cons (range start stop step)
                           xs)))))
> (map-range cons 2 2 '(a b c d))
((2 . a) (4 . b) (6 . c) (8 . d))

> (map-range list 5 5 '(a b c d) '(one two three four) '(w x y z))
((5 a one w) (10 b two x) (15 c three y) (20 d four z))

> (map-range cons 2 -2 '(a b c d))
((2 . a) (0 . b) (-2 . c) (-4 . d))

> (map-range list 5 -5 '(a b c d) '(one two three four) '(w x y z))
((5 a one w) (0 b two x) (-5 c three y) (-10 d four z))

关于scheme - LISP 中以索引作为参数之一的映射函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61397203/

相关文章:

clojure - 为什么 miniKanren 名称总是以 `o` 结尾?

functional-programming - 创建元素出现在列表中的所有索引列表的函数

macros - 定义一个宏来获取输入流

file-io - Racket 中的文件输出有最大长度吗?

recursion - 合并 2 组间隔

具有多个 ...s 的 Racket 语法模式

lambda - Scheme/Racket 中 let 的 lambda 定义是什么?

list - 获取值未列出 lisp 中的缺点

scheme - 在 Racket 中使用 stop-when

recursion - SICP 中的迭代阶乘过程