Emacs Lisp for Perl Programmers

Perl Emacs Lisp
Forcing variable declarations. use strict; No equivalent.
A typo in a variable name, silently creates a new global with default value of nil.
Global variables our $global="initial definition"; (defvar global "initial definition"
   "A global variable for doing stuff globally.")

(defcustom user_parameter "default setting"
   "Change this if you disagree with my default, fool")
Dynamic scoping
{ local $variable1 = "hey";
  local $variable2 = "ho";
 }
(let ( (variable1 "hey")
        (variable2 "ho")
    ))

Or the less surprising:

(let* (  (variable1 "hey")
          (variable2 "ho")
          (variable3 variable2)
    ))
Lexical scoping
{ my $lexical_dis = 1;
  my $lexical_luthor = 2;
  ...
 }
(require 'cl)
(lexical-let ((lexical-dis 1)
                  (lexical-luthor 2))
   ...
)
Subroutine with explicit return =item times_2

An inane example that multiples by 2.

=cut

sub times_2 {
    my $input = shift;
    my $output = 2 * $input;
    return $output
}
(defun times_2 (input)
       "An inane expample that multiplies by 2."
    (let* ((output * 2 input))
       output))
Basic output print STDERR "A message for the user\n";
print STDOUT "Some of the real output\n";
(message "A message for the user")
(insert "Some of the real output")
Formatted strings $output =
    sprintf("%s occurs %d times", $string, $number);
(setq output
(format "%s occurs %d times" string number))

Note: message does an implicit "format":
(message "%s occurs %d times" string number)
Assignment
$variable = "stuff";
(setq variable "stuff")
(set 'variable "stuff")
Assign to a variable indirectly
our $variable = "what";
our $indirect = "variable";
{ no strict "refs";
  ${ $indirect } = "hey";
  print "well, $variable \n";
 }
## prints "well, hey"
(let* (  (variable  "what")
           (indirect  'variable) )  ; Note *single* quote
   (set indirect "hey")
   (message "well, %s" variable))
;; prints "well, hey"

A difference from perl: we can't just use the variable name quoted as a string, in elisp, indirect assignment requires a variable's symbol (hence the use of the single-quote).
Quoting a String
'quotation'
"quotation"
Interpolated quotes
"Eat my $madlibs, okay?"
No real equivalent in elisp (no sigils).
This works for environment variables:
  (setq script-location
     (substitute-in-file-name "$HOME/bin"))
String concatenation $newstring = "Eat my" . $madlibs . ", okay?"; (setq newstring
    (concat "Eat my" madlibs ", okay?"))
String length $len = length( $stringy ); (setq len (length stringy))
Length of an array/list $len = scalar( @items );
    or
$len = $#items + 1;
(setq len (length items)) ;; something elisp got right
Error messages Undefined subroutine &main:: ... called

Global symbol " ... " requires explicit package name

bash: ... Permission denied
(You forgot to make it "executable".)
Symbol's definition as function is void: ...

Symbol's value as variable is void: ...

Wrong type argument: commandp, ...
(You forgot the "(interactive)".)
Evaluating code stored in a variable:
$code = "somefunc( $blah[2] );"
eval $code;
(setq code '(somefunc (car (cdr blah))))
(eval code)

Note: Single quote delays evaluation.

A more exact parallel, with the code in the form of a string:
(setq code-string "(somefunc (car (cdr blah)))")
(eval (read code-string)
Trapping errors use Carp;
eval {
     my $ratio = $a / $b;
}
if ($@) {
     carp "Problem with ratio of $a and $b: $@";
}
(condition-case nil
       (setq ratio (/ a b))
     (error
       (message "Problem with integer divide of %s by %s"
         (pp-to-string a)
         (pp-to-string b))))

Closures
A perl closure example None in elisp
repeat/untill
use Term::ReadKey;
$message = "Please input the name: ";
do {{
    print $message, "\n";
    $name = ReadLine(0);
    chomp($name);
   $message = "Not valid, try again: ";
   }} until (not (-e $name))
  (setq message = "Please input the name: )
  (setq default-location
        (file-name-as-directory default-location))
  (while (progn
           (setq filename
                 (expand-file-name
                  (read-file-name message default-location)))
           (setq message
                 "Not valid, try again: " )
           (file-exists-p filename)))
;; note: that's an empty while loop, all the code is in the conditional
Conditionals if ( is_first_time ) {
    print STDERR "This is the FIRST time.";
    print "we use this string the FIRST time.";
 } else {
    print STDERR "This is the SECOND time.";
    print "we use this string the SECOND time.";
 }
 (if (first-time-p)
        (progn ;; needed to glue together multi-line then clause
             (message "This is the FIRST time.")
             (insert "we use this string the FIRST time.")
             (insert "\n"))
      ;; the else clause has an "implicit progn"
      (message "This is the SECOND time")
      (insert "we use this string the SECOND time.")
      (insert "\n"))
elsif
No elisp equivalent, but see "cond" below...
case statement
use 5.10.0;
use feature "switch";
my $test_this = "something else";
given ( $test_this ) {
    when ( /something/ )
       {  print "case 1"  }
    when ( "case two string" )
       {  print "case 2"  }
    default
       {  print "default"  }
};

(cond ((string-match "something" test_this)
       (message "case 1"))
      ((string= test_this "case two string")
       (message "case 2"))
      (t
       (message "default")))
logical operators for short-circuit/ lazy evaluation
( $DEBUG ) and print STDERR "Problem!\n";

close $fh or die;
(and debug_flag (message "Problem!")

(or (...) (...))
For each item in a list, do something foreach $item ( @item_list ) {
   ...
 }
(require 'cl)
(dolist (item item-list)
  ...)

Alternately:
(mapc
 (lambda (item)
  ...
  ) item-list)
Do something to each item in a list @new-list =
   map {  some_func( $_ )  } @item_list;
(setq new-list
  (mapcar 'some-func item-list))
Do something to each item in a list (part II) my @new_list = map {  "Prefix: $_"  }
( "AAA", "BBB", "CCC" );
Result:
  ('Prefix: AAA', 'Prefix: BBB', 'Prefix: CCC');
(setq temp-list
   (mapcar (lambda (item)
     (concat "Prefix: " item))
      '("AAA" "BBB" "CCC")))
Result:
   ("Prefix: AAA" "Prefix: BBB" "Prefix: CCC")
Splitting a string @directory_levels = split m{ / }x, $filename;
(setq directory-levels-list (split-string filename "[/]" ))

;; even more perlish (it has a limit parameter)
(require 'dired-aux)
(setq limit 4)
(setq octets
     (dired-split "[\\.]" ip-address limit))
Joining strings join(  "/", @directory-levels )
(mapconcat 'identity directory-levels "/")
  ;; Note: identity is the way to do nothing
Regexps:
(this|that)

$pattern = "<\s*(.*?)\s*>";

\(this\|that\)

(setq pattern "<[  \t]*\\(.*?\\)[  \t]*>")
  ;; Note \( gets a second slash, but \t does not
String matching: Perl defaults to $_ and emacs to the current buffer
undef $/;
$_ = <>;
m{  $pattern  }xms;
$first_capture = $1;
(set-buffer some-stuff)
(if (re-search-forward pattern nil t) ;; uses current buffer
    (setq first-capture (match-string 1)))
   ;; match-string defaults to match data from current buffer.
Matching inside a given string if(  $some_string =~ m{  $pattern  }x  ) {
    $first_capture = $1;
}
(string-match pattern some-string)
(setq first_capture (match-string 1 some-string))
   ;; need to use string name again with match-string
Making a global change $string =~ s/TAG/current value/g;
(setq string
    (replace-regexp-in-string "TAG" "current value" string)

Alternately, working in the current buffer:

(while (re-search-forward "TAG" nil t)
    (replace-match "current value" nil nil))
comparison operators ( $num1 == $num2 )
( $str1 eq $str2 )

( $ref1 eq $ref2 ) # identical reference ( == would work too)

use Data::Compare; # Note: CPAN module, not core
if( Compare(  $ref1, $ref2  ) ){
    ...
}
(= num1 num2)
(string= str1 str2)

(eq emacs_object1 emacs_object2) ;; same object


(if (equal structure1 structure2)
    ( ... )
Natural data structures arrays and hashes
lists and alists (and hashes. and property lists. and...) Note: alists are ordered hashes with slow look-up times.
I talk about this some more: over here.





Further discussion of Emacs Lisp: Devnotes
Joseph Brenner, Created: February 26, 2004     Revised: August 17, 2010
Thanks to Shawn Betts for some corrections.