Функция карты с индексом в качестве одного из аргументов в LISP - PullRequest
2 голосов
/ 24 апреля 2020

Существует ли какая-либо встроенная функция в языках 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))

Очевидно, что это не очень эффективная реализация и не поддерживает несколько списков в качестве аргументов, как это делает обычная карта.

Ответы [ 3 ]

3 голосов
/ 24 апреля 2020

Ракетка


Вы можете написать очень простую версию map-index, используя процедуру Racket range и сопоставив результат:

(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 для нескольких списков, вы можете передать аргументы списка в необязательный параметр. Здесь apply применяет процедуру map к списку, составленному из функции f, списков ввода и списка range:

(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, используется тот же подход для решения Ракетки выше могут быть использованы схемы:

(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))

A 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))
3 голосов
/ 24 апреля 2020

Не совсем, но есть другие вещи для подобных целей, например for/list с in-naturals или in-indexed.

Например, вместо (map-index f lst), шаблон будет

(for/list ([x lst] [i (in-naturals)]) (f x i))

или

(for/list ([(x i) (in-indexed lst)]) (f x i))

И любой из этих шаблонов может быть использован для реализации map-index, а также комбинация map и build-list.

Итерационные формы Racket, такие как for/list, более гибкие, чем фиксированный набор функций, подобных карте.

Конкретные примеры :

> (for/list ([x '(a b c d)] [i (in-naturals)]) (cons x i))
'((a . 0) (b . 1) (c . 2) (d . 3))
> (for/list ([(x i) (in-indexed '(a b c d))]) (cons x i))
'((a . 0) (b . 1) (c . 2) (d . 3))

Или, если вам все еще нужна функция map-index, вы можете определить ее более кратко, используя это.

> (define (map-index f lst)
    (for/list ([(x i) (in-indexed lst)]) (f x i)))
> (map-index cons '(a b c d))
'((a . 0) (b . 1) (c . 2) (d . 3))
0 голосов
/ 24 апреля 2020

Посмотрите на индексированные по карте в da sh для Emacs. Он делает то, что вы ищете. https://github.com/magnars/dash.el

...