Como posso mapear um vetor e obter um vetor?


15

A única coisa que descobri que funciona é

(eval `(vector ,@(mapcar #'1+ [1 2 3 4])))
=> [2 3 4 5]

mas que parece muito muito complicado para ser a maneira 'certa'.

Respostas:


19

Use cl-map, em vez disso:

(cl-map 'vector #'1+ [1 2 3 4])

Um pouco mais de fundo: cl-mapé a função Common Lispmap que generaliza para tipos de sequência:

(cl-map 'vector #'1+ '[1 2 3 4]) ;; ==> [2 3 4 5]
(cl-map 'list   #'1+ '(1 2 3 4)) ;; ==> (2 3 4 5)
(cl-map 'string #'upcase "abc")  ;; ==> "ABC"

Também pode converter entre tipos de sequência (por exemplo, aqui, a entrada é uma lista e a saída é um vetor):

(cl-map 'vector #'1+ '(1 2 3 4)) ;; ==> [2 3 4 5]

1
18 segundos, o 'vencedor' :) As clbibliotecas não dão avisos ao compilador? (Principalmente porque a FSF é detestável?)
— Sean Allred

1
FWIW, acho que os problemas de compilação de bytes estavam relacionados à clbiblioteca antiga , e não à cl-libbiblioteca rejeitada . Por exemplo, não recebo nenhum aviso quando (defun fnx () (cl-map 'vector #'1+ '[1 2 3 4]))e quando (byte-compile 'fnx).
— Dan

2
Mesmo se você usar o cl-lib de compatibilidade, acho que você receberá avisos no emacs mais antigo (24.2). Eu não me preocuparia com isso, você tem que escolher suas batalhas.
— Malabarba

16

Desde que fui derrotado por 18 segundos, eis uma maneira mais simples e segura de fazê-lo sem a biblioteca cl. Também não avalia os elementos.

(apply #'vector (mapcar #'1+ [1 2 3 4])) ;; => [2 3 4 5]

Isso também é muito bom! Re: seu comentário anterior sobre o Emacs mais antigo: parece especialmente útil se você precisar antecipar usuários legados. Parece mais útil se você precisar usá-lo apenas em alguns pontos; nesse momento, você pode trocar o pequeno inconveniente contra evitar a cl-libdependência.
— Dan

1
Muito bacana !! Eu não pensei em usar apply.
— Sean Allred

Eu acho que (apply #'vector ...)pode ser um pouco mais rápido, mas, para ser completo, também pode ser substituído por (vconcat ...).
— Basil

1

A variante local não tão elegante para o caso que o vetor original não é mais necessário posteriormente e a alocação de memória é de tempo crítico (por exemplo, o vetor é grande).

(setq x [1 2 3 4])

(cl-loop for var across-ref x do
         (setf var (1+ var)))

O resultado é armazenado em x. Se você precisar retornar o formulário xno final, poderá adicionar finally return xo seguinte:

(cl-loop for var across-ref x do
         (setf var (1+ var))
         finally return x)

1

Para completar, use seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)

Há uma resposta excluída do Fólkvangr 12/11/2018 com a mesma seq-intolinha exata . O usuário excluiu sua resposta pelo seguinte motivo: "Minha solução é menos relevante porque a biblioteca seq usa as extensões Common Lisp subjacentes. - Fólkvangr 16 de maio às 8:53"
— Tobias

@ Tobias Acho que discordo dessa lógica. Tudo acabará usando vconcat ou vetor de qualquer maneira, mas os diferentes paradigmas de interface são úteis para ter em registro.
— Sean Allred

Sem problemas. Acabei de ver a resposta excluída do Fólkvangr (quase) igual à sua e queria notificá-lo. Por alguma razão vendo respostas excluídos requer 10000 rep :-(.
— Tobias

@Tobias sim, eu nunca entendi por que esses privilégios eram específicas do local :-)
— Sean Allred

0

Você pode usar loop

(let ((v (vector 1 2 3 4)))
  (dotimes (i (length v))
    (aset v i (1+ (aref v i))))
  v)
;; => [2 3 4 5]

Às vezes, você não deseja modificar o vetor original, pode fazer uma cópia

(let* ((v0 (vector 1 2 3 4))
       (v (copy-sequence v0)))
  (dotimes (i (length v))
    (aset v i (1+ (aref v i))))
  (list v0 v))
;; => ([1 2 3 4] [2 3 4 5])

ou crie um novo vetor do zero

(let* ((v0 (vector 1 2 3 4))
       (v (make-vector (length v0) nil)))
  (dotimes (i (length v))
    (aset v i (1+ (aref v0 i))))
  (list v0 v))
;; => ([1 2 3 4] [2 3 4 5])
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.