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'.

Sean Allred
fonte

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]
Dan
fonte
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]
Malabarba
fonte
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)
Tobias
fonte
1

Para completar, use seq:

(require 'seq)
(seq-into (seq-map #'1+ [1 2 3 4]) 'vector)
Sean Allred
fonte
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])
xuchunyang
fonte