Practical example of Lisp's flexibility? [clos

2019-03-07 10:53发布

Someone is trying to sell Lisp to me, as a super powerful language that can do everything ever, and then some.

Is there a practical code example of Lisp's power?
(Preferably alongside equivalent logic coded in a regular language.)

18条回答
家丑人穷心不美
2楼-- · 2019-03-07 11:49

You may find this article helpful: http://www.defmacro.org/ramblings/lisp.html

That said, it's very, very hard to give short, practical examples of Lisp's power because it really shines only in non-trivial code. When your project grows to a certain size, you will appreciate Lisp's abstraction facilities and be glad that you've been using them. Reasonably short code samples, on the other hand, will never give you a satisfying demonstration of what makes Lisp great because other languages' predefined abbreviations will look more attractive in small examples than Lisp's flexibility in managing domain-specific abstractions.

查看更多
我只想做你的唯一
3楼-- · 2019-03-07 11:52

The best example I can think of that is widely available is the book by Paul Graham, On Lisp. The full PDF can be downloaded from the link I just gave. You could also try Practical Common Lisp (also fully available on the web).

I have a lot of unpractical examples. I once wrote a program in about 40 lines of lisp which could parse itself, treat its source as a lisp list, do a tree traversal of the list and build an expression that evaluated to WALDO if the waldo identifier existed in the source or evaluate to nil if waldo was not present. The returned expression was constructed by adding calls to car/cdr to the original source that was parsed. I have no idea how to do this in other languages in 40 lines of code. Perhaps perl can do it in even fewer lines.

查看更多
Deceive 欺骗
4楼-- · 2019-03-07 11:55

See how you can extend Common Lisp with XML templating: cl-quasi-quote XML example, project page,

(babel:octets-to-string
 (with-output-to-sequence (*html-stream*)
   <div (constantAttribute 42
         someJavaScript `js-inline(print (+ 40 2))
         runtimeAttribute ,(concatenate 'string "&foo" "&bar"))
     <someRandomElement
       <someOther>>>))

 =>

 "<div constantAttribute=\"42\"
       someJavaScript=\"javascript: print((40 + 2))\"
       runtimeAttribute=\"&amp;foo&amp;bar\">
    <someRandomElement>
      <someOther/>
    </someRandomElement>
  </div>"

This is basically the same thing as Lisp's backtick reader (which is for list quasi quoting), but it also works for various other things like XML (installed on a special <> syntax), JavaScript (installed on `js-inline), etc.

To make it clear, this is implemented in a user library! And it compiles the static XML, JavaScript, etc. parts into UTF-8 encoded literal byte arrays that are ready to be written to the network stream. With a simple , (comma) you can get back to lisp and interleave runtime generated data into the literal byte arrays.

This is not for the faint of heart, but this is what the library compiles the above into:

(progn
 (write-sequence
  #(60 100 105 118 32 99 111 110 115 116 97 110 116 65 116 116 114 105 98
    117 116 101 61 34 52 50 34 32 115 111 109 101 74 97 118 97 83 99 114
    105 112 116 61 34 106 97 118 97 115 99 114 105 112 116 58 32 112 114
    105 110 116 40 40 52 48 32 43 32 50 41 41 34 32 114 117 110 116 105
    109 101 65 116 116 114 105 98 117 116 101 61 34)
  *html-stream*)
 (write-quasi-quoted-binary
  (let ((*transformation*
          #<quasi-quoted-string-to-quasi-quoted-binary {1006321441}>))
    (transform-quasi-quoted-string-to-quasi-quoted-binary
      (let ((*transformation*
               #<quasi-quoted-xml-to-quasi-quoted-string {1006326E51}>))
        (locally
            (declare (sb-ext:muffle-conditions sb-ext:compiler-note))
         (let ((it (concatenate 'string "runtime calculated: " "&foo" "&bar")))
           (if it
               (transform-quasi-quoted-xml-to-quasi-quoted-string/attribute-value it)
               nil))))))
  *html-stream*)
 (write-sequence
  #(34 62 10 32 32 60 115 111 109 101 82 97 110 100 111 109 69 108 101 109
    101 110 116 62 10 32 32 32 32 60 115 111 109 101 79 116 104 101 114 47
    62 10 32 32 60 47 115 111 109 101 82 97 110 100 111 109 69 108 101 109
    101 110 116 62 10 60 47 100 105 118 62 10)
  *html-stream*)
 +void+)

For reference, the two big byte vectors in the above look like this when converted to string:

"<div constantAttribute=\"42\"
      someJavaScript=\"javascript: print((40 + 2))\"
      runtimeAttribute=\""

And the second one:

"\">
 <someRandomElement>
    <someOther/>
  </someRandomElement>
</div>"

And it combines well with other Lisp structures like macros and functions. now, compare this to JSPs...

查看更多
萌系小妹纸
5楼-- · 2019-03-07 11:56

I like this macro example from http://common-lisp.net/cgi-bin/viewcvs.cgi/cl-selenium/?root=cl-selenium It's a Common Lisp binding to Selenium (a web browser test framework), but instead of mapping every method, it reads Selenium's own API definition XML document at compile time and generates the mapping code using macros. You can see the generated API here: common-lisp.net/project/cl-selenium/api/selenium-package/index.html

This is essentially driving macros with external data, which happens to be an XML document in this case, but could have been as complex is reading from a database or network. This is the power of having the entire Lisp environment available to you at compile time.

查看更多
【Aperson】
6楼-- · 2019-03-07 11:57

There are plenty of killer features in Lisp, but macros is one I love particularily, because there's not really a barrier anymore between what the language defines and what I define. For example, Common Lisp doesn't have a while construct. I once implemented it in my head, while walking. It's straightforward and clean:

(defmacro while (condition &body body)
  `(if ,condition
       (progn
         ,@body
         (do nil ((not ,condition))
           ,@body))))

Et voilà! You just extended the Common Lisp language with a new fundamental construct. You can now do:

(let ((foo 5))
  (while (not (zerop (decf foo)))
    (format t "still not zero: ~a~%" foo)))

Which would print:

still not zero: 4
still not zero: 3
still not zero: 2
still not zero: 1

Doing that in any non-Lisp language is left as an exercise for the reader...

查看更多
爱情/是我丢掉的垃圾
7楼-- · 2019-03-07 11:58

@Mark,

While there is some truth to what you are saying, I believe it is not always as straight forward.

Programmers and people in general don't always take the time to evaluate all the possibilities and decide to switch languages. Often It's the managers that decide, or the schools that teach the first languages ... and programmers never have the need to invest enough amount of time to get to a certain level were they can decide this language saves me more time than that language.

Plus you have to admit that languages that have the backing of huge commercial entities such as Microsoft or Sun will always have an advantage in the market compared to languages without such backing.

In order to answer the original question, Paul Graham tries to give an example here even though I admit it is not necessarily as practical as I would like :-)

查看更多
登录 后发表回答