2
\$\begingroup\$

I just found this post with vim (https://stackoverflow.com/questions/704130/can-i-transpose-a-file-in-vim) and i just wanted to do the same thing with elisp by creating a little script to reordenate all the characters from a buffer text into a vertical position.

As the post said, the original buffer might be:

THE DAY WAS LONG 
THE WAY WAS FAST

and it would become:

TT
HH
EE

DW
AA
YY

WW
AA
SS

LF
OA
NS
GT

I write this little emacs script to first read each buffer lines in a list (removing spaces), and after using a zip function to transpose each characters between lists, and at the end iterate my list to print it in x columns.

(require 'subr-x)
(defun read-buffer-lines (buf)
  (let ((lines '()))
    (with-current-buffer buf
      (save-excursion
        (goto-char (point-min))
        (while (not (eobp))
          (if (not (string-equal "" (buffer-substring (point) (point-at-eol))))
              (push
               (delete " " (delete "" (split-string
                                       (string-trim (buffer-substring (point) (point-at-eol)))
                                       "")))
                  lines)
              )
            (beginning-of-line 2))
        (erase-buffer) ;; clean buffer
        (nreverse lines)
        ))))



(defun zip_list (lst)
  (let ((res))
    (while (-none? 'null lst)
      (setq res (cons (mapcar 'car lst) res))
      (setq lst (mapcar 'cdr lst)))
    (nreverse res)
    ))


(let* ((buf "*scratch*")
       (a (zip_list (read-buffer-lines buf))))
  (with-current-buffer buf
    (while a
      (progn
        (insert (mapconcat 'identity (car a) ""))
        (newline)
        (setq a (cdr a))))))
\$\endgroup\$
  • \$\begingroup\$ Why would you save excursion and erase buffer? :) Where would Emacs have to put the mouse after the buffer is erased? Other than that: there's an easier way to split the contents of the buffer into lines: (split-string (buffer-string) "\n"). I wouldn't use a list of list to model a matrix (which you want to transpose). A vector of vectors would've made the task a lot easier. \$\endgroup\$ – wvxvw Dec 4 '17 at 13:20
  • \$\begingroup\$ agree. good point. \$\endgroup\$ – papachan Dec 4 '17 at 19:43
3
\$\begingroup\$

Few general points about your code:

  1. (while condition &body) form already treats &body as an implicit progn. There's no reason to wrap it in a progn.
  2. insert accepts multiple arguments. So, you could write (insert (mapconcat ...) "\n") instead of calling (insert ...) followed by (newline).
  3. Traditionally, Lisp functions use - when the name of the function consists of multiple words, so, it would be better to call zip-list, not zip_list.
  4. (let (res) ...) is exactly the same as (let ((res)) ...) but shorter.
  5. (setq ...) can handle multiple assignments, they are executed in order they are written, so (setq a b c a) will assign b to c. No reason to write (setq a b) (setq c a).
  6. split-string has argument OMIT-NULLS that controls whether empty strings are included in the results. It would be better to rely on this argument than to post-process the results.
  7. As mentioned in the comments, there's an easier, more idiomatic way to obtain all lines of text from the buffer: (split-string (buffer-string) "\n").

Finally, unless only for the sake of an exercise in writing the zip-list function, the transpose operation calls for something like vector, not a list. Below is a possible alternative which uses vectors to perform this operation:

(defun wvxvw/transpose-buffer ()
  (interactive)
  (let* ((lines (split-string (buffer-string) "\n"))
         (max-length
          (cl-loop for line in lines
                   maximize (max (length line))))
         (src (cl-coerce lines 'vector))
         (dst (cl-loop with matrix = (make-vector max-length nil)
                       with len = (length src)
                       for i below max-length do
                       (aset matrix i (make-vector len ?\ ))
                       finally (cl-return matrix))))
    (cl-loop for i upfrom 0
             for line across src do
             (cl-loop for j upfrom 0
                      for c across line do
                      (aset (aref dst j) i c)))
    (erase-buffer)
    (cl-loop for line across dst do
             (insert (cl-coerce line 'string) "\n"))))
\$\endgroup\$
  • \$\begingroup\$ nice work ! i just tried it and it work like a charm ! thank you for all your suggestions. there are very useful for me. \$\endgroup\$ – papachan Dec 4 '17 at 19:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.