Lisp의 유연성에 대한 실제적인 예?
누군가 저에게 Lisp를 팔려고합니다. 모든 것을 할 수있는 초강력 언어로, 그 다음에는 일부를 할 수 있습니다.
Lisp의 힘에 대한 실용적인 코드 예제가 있습니까?
(일반 언어로 코딩 된 동등한 로직과 함께 사용하는 것이 좋습니다.)
나는 매크로를 좋아합니다.
다음은 LDAP에서 사람들의 속성을 제거하는 코드입니다. 나는 그 코드를 주변에두고 다른 사람들에게 유용 할 것이라고 생각했습니다.
어떤 사람들은 예상되는 매크로의 런타임 페널티에 대해 혼란스러워서 마지막에 명확히하기위한 시도를 추가했습니다.
처음에는 중복이 있었다
(defun ldap-users ()
(let ((people (make-hash-table :test 'equal)))
(ldap:dosearch (ent (ldap:search *ldap* "(&(telephonenumber=*) (cn=*))"))
(let ((mail (car (ldap:attr-value ent 'mail)))
(uid (car (ldap:attr-value ent 'uid)))
(name (car (ldap:attr-value ent 'cn)))
(phonenumber (car (ldap:attr-value ent 'telephonenumber))))
(setf (gethash uid people)
(list mail name phonenumber))))
people))
"let 바인딩"을 LET 형식 외부에서 사라지는 지역 변수로 생각할 수 있습니다. 바인딩의 형식을 확인하십시오. 매우 유사하며 LDAP 엔티티의 속성과 값을 바인딩 할 이름 ( "로컬 변수") 만 다릅니다. 유용하지만 약간 장황하고 중복이 포함되어 있습니다.
아름다움을위한 탐구
자, 우리가 모든 중복을 가질 필요가 없다면 좋지 않을까요? 일반적인 관용구는 WITH -... 매크로로, 값을 가져올 수있는 표현식을 기반으로 값을 바인딩합니다. 이와 같이 작동하는 자체 매크로 인 WITH-LDAP-ATTRS를 소개하고 원래 코드로 대체하겠습니다.
(defun ldap-users ()
(let ((people (make-hash-table :test 'equal))) ; equal so strings compare equal!
(ldap:dosearch (ent (ldap:search *ldap* "(&(telephonenumber=*) (cn=*))"))
(with-ldap-attrs (mail uid name phonenumber) ent
(setf (gethash uid people)
(list mail name phonenumber))))
people))
여러 줄이 갑자기 사라지고 한 줄로 바뀌는 것을 보셨나요? 어떻게하나요? 물론 매크로 사용-코드를 작성하는 코드! Lisp의 매크로는 전처리기를 사용하여 C / C ++에서 찾을 수있는 것과 완전히 다른 동물입니다. 여기에서 Lisp 코드 를 생성 하는 실제 Lisp 코드 ( #definecpp 의 플러 프가 아님)를 다른 것보다 먼저 실행할 수 있습니다. 코드가 컴파일됩니다. 매크로는 실제 Lisp 코드, 즉 일반 기능을 사용할 수 있습니다. 본질적으로 제한이 없습니다.
추악한 제거
그래서 이것이 어떻게 이루어 졌는지 봅시다. 하나의 속성을 대체하기 위해 함수를 정의합니다.
(defun ldap-attr (entity attr)
`(,attr (car (ldap:attr-value ,entity ',attr))))
역 따옴표 구문은 약간 복잡해 보이지만 수행하는 작업은 쉽습니다. 당신은 LDAP-attrs에 전화를 할 때,이 포함 된 목록을 뱉어 것이다 값 의 attr다음 (즉, 쉼표의), car( "목록의 첫 번째 요소"실제로 (죄수 쌍), 및라는 함수는 사실에있다 first에서 반환 된 목록의 첫 번째 값을받는)를 사용할 수도 있습니다 ldap:attr-value. 이것은 우리가 코드를 컴파일 할 때 실행하고 싶은 코드가 아니기 때문에 (애트리뷰트 값을 얻는 것은 프로그램 을 실행할 때 우리가 원하는 것입니다 ), 호출 전에 쉼표를 추가하지 않습니다.
어쨌든. 나머지 매크로로 이동합니다.
(defmacro with-ldap-attrs (attrs ent &rest body)
`(let ,(loop for attr in attrs
collecting `,(ldap-attr ent attr))
,@body))
,@- 구문 대신 실제 목록으로, 목록 어딘가의 내용을 넣어하는 것입니다.
결과
이것이 옳은 일을 제공하는지 쉽게 확인할 수 있습니다. 매크로는 종종 이런 방식으로 작성됩니다. 더 간단하게 만들고 싶은 코드 (출력), 대신 작성하려는 코드 (입력)로 시작한 다음 입력이 올바른 출력을 제공 할 때까지 매크로를 몰딩하기 시작합니다. 이 함수 macroexpand-1는 매크로가 올바른지 알려줍니다.
(macroexpand-1 '(with-ldap-attrs (mail phonenumber) ent
(format t "~a with ~a" mail phonenumber)))
평가하다
(let ((mail (car (trivial-ldap:attr-value ent 'mail)))
(phonenumber (car (trivial-ldap:attr-value ent 'phonenumber))))
(format t "~a with ~a" mail phonenumber))
확장 된 매크로의 LET 바인딩을 처음의 코드와 비교하면 동일한 형식임을 알 수 있습니다!
컴파일 시간 vs 런타임 : 매크로 vs 함수
매크로는 컴파일 타임에 실행되는 코드로 , 원하는대로 일반 함수 나 매크로를 호출 할 수 있다는 추가 트위스트가 추가되었습니다 ! 인수를 취하고 일부 변환을 적용한 다음 컴파일러에 결과 s-exp를 제공하는 멋진 필터에 지나지 않습니다.
기본적으로 언어의 저수준 프리미티브 대신 문제 영역에서 찾을 수있는 동사로 코드를 작성할 수 있습니다! 어리석은 예로서 다음을 고려하십시오 ( when아직 내장되어 있지 않은 경우 ).
(defmacro my-when (test &rest body)
`(if ,test
(progn ,@body)))
if는 브랜치에서 하나의 양식 만 실행할 수있는 기본 제공 기본 요소이며 둘 이상의 양식을 사용하려면 다음을 사용해야합니다. progn::
;; one form
(if (numberp 1)
(print "yay, a number"))
;; two forms
(if (numberp 1)
(progn
(assert-world-is-sane t)
(print "phew!"))))
우리의 새로운 친구와 함께, my-when우리는 거짓 지점이없는 경우에, 우리는 모두가) 더 적절한 동사를 사용할 수 있으며, b)는 암시 적 순서 연산자를 추가, 즉 progn::
(my-when (numberp 1)
(assert-world-is-sane t)
(print "phew!"))
그러나 컴파일 된 코드에는를 포함하지 않습니다 my-when. 첫 번째 단계에서는 모든 매크로가 확장되어 런타임 패널티 가 발생 하지 않기 때문입니다 !
Lisp> (macroexpand-1 '(my-when (numberp 1)
(print "yay!")))
(if (numberp 1)
(progn (print "yay!")))
참고 macroexpand-1단지 확장의 한 단계를 수행; 확장이 더 아래로 계속되는 것이 가능합니다 (실제로는 대부분!). 그러나 결국에는 그리 흥미롭지 않은 컴파일러 특정 구현 세부 사항에 도달하게 될 것입니다. 그러나 결과를 계속 확장하면 결국 더 자세한 정보를 얻거나 입력 s-exp를 다시 얻을 수 있습니다.
그것을 명확히하는 희망. 매크로는 강력한 도구이며 내가 좋아하는 Lisp의 기능 중 하나입니다.
널리 사용 가능하다고 생각할 수있는 가장 좋은 예는 Paul Graham, On Lisp 의 책 입니다. 전체 PDF는 방금 제공 한 링크에서 다운로드 할 수 있습니다. Practical Common Lisp (웹에서도 완벽하게 사용 가능)를 사용해 볼 수도 있습니다 .
비실용적 인 예가 많이 있습니다. 나는 약 40 줄의 lisp로 프로그램을 작성했는데,이 프로그램은 스스로를 파싱하고, 소스를 lisp 목록으로 취급하고, 목록의 트리 순회를 수행하고, waldo 식별자가 소스에 존재하거나 다음과 같이 평가되는 경우 WALDO로 평가되는 표현식을 작성합니다. waldo가 없으면 nil. 반환 된 표현식은 구문 분석 된 원래 소스에 car / cdr에 대한 호출을 추가하여 구성되었습니다. 40 줄의 코드로 다른 언어로이 작업을 수행하는 방법을 모릅니다. 아마도 Perl은 더 적은 라인으로 그것을 할 수 있습니다.
이 기사가 도움이 될 수 있습니다 : http://www.defmacro.org/ramblings/lisp.html
즉, Lisp의 힘에 대한 짧고 실용적인 예제를 제공하는 것은 매우 어렵습니다. 왜냐하면 그것은 사소하지 않은 코드에서만 빛을 발하기 때문입니다. 프로젝트가 특정 크기로 커지면 Lisp의 추상화 기능에 감사하고이를 사용하고 있다는 사실에 기뻐할 것입니다. 반면에 합리적으로 짧은 코드 샘플은 다른 언어의 사전 정의 된 약어가 도메인 별 추상화를 관리하는 Lisp의 유연성보다 작은 예에서 더 매력적으로 보이기 때문에 Lisp를 훌륭하게 만드는 것에 대한 만족스러운 데모를 제공하지 않습니다.
실제로 좋은 실용적인 예는 Lisp LOOP 매크로입니다.
http://www.ai.sri.com/pkarp/loop.html
LOOP 매크로는 단순히 Lisp 매크로입니다. 그러나 기본적으로 미니 루핑 DSL (Domain Specific Language)을 정의합니다.
이 작은 튜토리얼을 살펴보면 (초보자라도) 코드의 어떤 부분이 루프 매크로의 일부인지, 그리고 어떤 부분이 "정상적인"Lisp인지 알기가 어렵다는 것을 알 수 있습니다.
그리고 그것은 Lisps 표현력의 핵심 구성 요소 중 하나이며, 새로운 코드는 실제로 시스템과 구별 될 수 없습니다.
예를 들어, Java에서는 프로그램의 어떤 부분이 표준 Java 라이브러리에서 나온 것인지 자신의 코드 또는 타사 라이브러리에서 나온 것인지 (한눈에) 알 수 없지만 코드의 어떤 부분을 알 수 있습니다. 클래스에 대한 단순한 메소드 호출이 아닌 Java 언어입니다. 물론, 그것은 모두 "자바 언어"이지만 프로그래머로서 여러분은 애플리케이션을 클래스와 메소드의 조합 (그리고 이제 주석)으로 만 표현하는 것으로 제한됩니다. Lisp에서는 말 그대로 모든 것이 손에 들어갑니다.
Common Lisp를 SQL에 연결하려면 Common SQL 인터페이스를 고려하십시오. 여기 http://clsql.b9.com/manual/loop-tuples.html 에서는 SQL 바인딩을 "일급 시민"으로 만들기 위해 CL 루프 매크로를 확장하는 방법을 보여줍니다.
"[select [first-name] [last-name] : from [employee] : order-by [last-name]]"과 같은 구문도 관찰 할 수 있습니다. 이것은 CL-SQL 패키지의 일부이며 "리더 매크로"로 구현됩니다.
Lisp에서는 데이터 구조, 제어 구조 등과 같은 새로운 구조를 만들기 위해 매크로를 만들 수있을뿐만 아니라 판독기 매크로를 통해 언어 구문을 변경할 수도 있습니다. 여기에서는 리더 매크로 ( '['기호)를 사용하여 SQL 모드로 들어가 SQL이 다른 많은 언어 에서처럼 원시 문자열처럼 작동하지 않고 삽입 된 SQL처럼 작동하도록합니다.
애플리케이션 개발자로서 우리의 임무는 프로세스와 구성을 프로세서가 이해할 수있는 형태로 변환하는 것입니다. 이는 컴퓨터 언어가 우리를 "이해하지 못하기"때문에 필연적으로 컴퓨터 언어에 "대화"해야한다는 것을 의미합니다.
Common Lisp는 위에서 아래로 애플리케이션을 빌드 할 수있을뿐만 아니라 언어와 환경을 끌어 올려 우리를 반쯤 만날 수있는 몇 안되는 환경 중 하나입니다. 양쪽 끝에서 코드를 작성할 수 있습니다.
이만큼 우아하지만 만병 통치약이 아닙니다. 분명히 언어와 환경 선택에 영향을 미치는 다른 요인이 있습니다. 그러나 그것은 확실히 배우고 놀만한 가치가 있습니다. Lisp를 배우는 것이 다른 언어에서도 프로그래밍을 발전시키는 좋은 방법이라고 생각합니다.
저는 Common Lisp Object System (CLOS)과 멀티 메서드를 좋아 합니다.
전부는 아니지만 대부분의 객체 지향 프로그래밍 언어에는 클래스 및 메서드의 기본 개념이 있습니다. Python 의 다음 스 니펫은 PeelingTool 및 Vegetable 클래스를 정의합니다 (방문자 패턴과 유사 함).
class PeelingTool:
"""I'm used to peel things. Mostly fruit, but anything peelable goes."""
def peel(self, veggie):
veggie.get_peeled(self)
class Veggie:
"""I'm a defenseless Veggie. I obey the get_peeled protocol
used by the PeelingTool"""
def get_peeled(self, tool):
pass
class FingerTool(PeelingTool):
...
class KnifeTool(PeelingTool):
...
class Banana(Veggie):
def get_peeled(self, tool):
if type(tool) == FingerTool:
self.hold_and_peel(tool)
elif type(tool) == KnifeTool:
self.cut_in_half(tool)
이 peel메서드를 PeelingTool에 넣고 Banana가 받아들이도록합니다. 그러나 PeelingTool 클래스에 속해야하므로 PeelingTool 클래스의 인스턴스가있는 경우에만 사용할 수 있습니다.
Common Lisp Object System 버전 :
(defclass peeling-tool () ())
(defclass knife-tool (peeling-tool) ())
(defclass finger-tool (peeling-tool) ())
(defclass veggie () ())
(defclass banana (veggie) ())
(defgeneric peel (veggie tool)
(:documentation "I peel veggies, or actually anything that wants to be peeled"))
;; It might be possible to peel any object using any tool,
;; but I have no idea how. Left as an exercise for the reader
(defmethod peel (veggie tool)
...)
;; Bananas are easy to peel with our fingers!
(defmethod peel ((veggie banana) (tool finger-tool))
(with-hands (left-hand right-hand) *me*
(hold-object left-hand banana)
(peel-with-fingers right-hand tool banana)))
;; Slightly different using a knife
(defmethod peel ((veggie banana) (tool knife-tool))
(with-hands (left-hand right-hand) *me*
(hold-object left-hand banana)
(cut-in-half tool banana)))
모든 것이 Turing 완전한 언어로 작성 될 수 있습니다. 언어 간의 차이점은 동등한 결과를 얻기 위해 얼마나 많은 농구를 통과해야 하는가입니다.
매크로 및 CLOS와 같은 기능을 갖춘 Common Lisp 와 같은 강력한 언어를 사용하면 너무 많은 후프를 뛰어 넘지 않고도 결과를 빠르고 쉽게 얻을 수 있으므로 수준 이하의 솔루션에 만족하거나 캥거루가 될 수 있습니다.
Lisp에는 많은 킬러 기능이 있지만, 매크로는 내가 특히 좋아하는 기능입니다. 언어가 정의하는 것과 내가 정의하는 것 사이에 더 이상 장벽이 없기 때문입니다. 예를 들어 Common Lisp에는 while 구문 이 없습니다 . 나는 한때 걷는 동안 내 머리 속에 그것을 구현했습니다. 간단하고 깔끔합니다.
(defmacro while (condition &body body)
`(if ,condition
(progn
,@body
(do nil ((not ,condition))
,@body))))
Et voilà! 방금 Common Lisp 언어를 새로운 기본 구조로 확장했습니다. 이제 다음을 수행 할 수 있습니다.
(let ((foo 5))
(while (not (zerop (decf foo)))
(format t "still not zero: ~a~%" foo)))
다음과 같이 인쇄됩니다.
still not zero: 4
still not zero: 3
still not zero: 2
still not zero: 1
Lisp가 아닌 언어로 그렇게하는 것은 독자를위한 연습으로 남겨집니다 ...
이 기사가 꽤 흥미로웠다.
이 기사의 저자 인 Brandon Corfman은 Java, C ++ 및 Lisp의 솔루션을 프로그래밍 문제와 비교 한 다음 C ++로 자신의 솔루션을 작성하는 연구에 대해 작성했습니다. 벤치 마크 솔루션은 Peter Norvig의 Lisp 45 줄 (2 시간 내에 작성)입니다.
Corfman은 솔루션을 C ++ / STL의 142 줄 미만으로 줄이는 것이 어렵다는 것을 알게되었습니다. 이유에 대한 그의 분석은 흥미로운 읽기입니다.
Lisp (및 Smalltalk ) 시스템 에 대해 내가 가장 좋아 하는 점은 그들이 살아 있다고 느끼는 것입니다. Lisp 시스템이 실행되는 동안 쉽게 조사하고 수정할 수 있습니다.
이상하게 들리면 Emacs를 시작 하고 Lisp 코드를 입력하십시오. 타이핑 C-M-x하고 보라! 방금 Emacs 내에서 Emacs를 변경했습니다. 실행하는 동안 모든 Emacs 기능을 계속해서 재정의 할 수 있습니다.
또 다른 한 가지는 코드 = 목록 동등성이 코드와 데이터 사이의 경계를 매우 얇게 만든다는 것입니다. 그리고 매크로 덕분에 언어를 확장하고 빠른 DSL을 만드는 것이 매우 쉽습니다 .
예를 들어, 코드가 생성 된 HTML 출력에 매우 가까운 기본 HTML 빌더를 코딩 할 수 있습니다.
(html
(head
(title "The Title"))
(body
(h1 "The Headline" :class "headline")
(p "Some text here" :id "content")))
=>
<html>
<head>
<title>The title</title>
</head>
<body>
<h1 class="headline">The Headline</h1>
<p id="contents">Some text here</p>
</body>
</html>
Lisp 코드에서 자동 들여 쓰기는 닫는 태그가 없다는 점을 제외하면 코드를 출력과 비슷하게 만듭니다.
http://common-lisp.net/cgi-bin/viewcvs.cgi/cl-selenium/?root=cl-selenium 의이 매크로 예제가 마음 에 듭니다. Selenium (웹 브라우저 테스트 프레임 워크)에 대한 Common Lisp 바인딩이지만 모든 메서드를 매핑하는 대신 컴파일 타임에 Selenium 자체 API 정의 XML 문서를 읽고 매크로를 사용하여 매핑 코드를 생성합니다. 여기에서 생성 된 API를 볼 수 있습니다. common-lisp.net/project/cl-selenium/api/selenium-package/index.html
이것은 본질적으로 외부 데이터로 매크로를 구동합니다.이 경우에는 XML 문서이지만 데이터베이스 나 네트워크에서 읽는 것처럼 복잡 할 수 있습니다. 이것이 컴파일 타임에 전체 Lisp 환경을 사용할 수있는 힘입니다.
XML 템플릿을 사용하여 Common Lisp를 확장 하는 방법을 참조하십시오 . cl-quasi-quote XML 예제 , 프로젝트 페이지 ,
(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=\"&foo&bar\">
<someRandomElement>
<someOther/>
</someRandomElement>
</div>"
이것은 기본적으로 Lisp의 백틱 리더 (목록 유사 인용 용)와 동일하지만 XML (특수 <> 구문에 설치됨), JavaScript (`js-inline에 설치됨) 등과 같은 다양한 다른 것들에서도 작동합니다. .
명확하게하기 위해 이것은 사용자 라이브러리 에서 구현됩니다 ! 또한 정적 XML, JavaScript 등의 부분을 네트워크 스트림에 쓸 준비가 된 UTF-8 인코딩 리터럴 바이트 배열 로 컴파일합니다 . 간단한 ,(쉼표)를 사용하여 lisp로 돌아가 런타임 생성 데이터를 리터럴 바이트 배열에 인터리브 할 수 있습니다.
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...
I was an AI student at MIT in the 1970s. Like every other student, I thought language was paramount. Nevertheless, Lisp was the primary language. These are some things I still think it is pretty good for:
Symbolic math. It is easy and instructive to write symbolic differentiation of an expression, and algebraic simplification. I still do those, even though I do them in C-whatever.
Theorem proving. Every now & then I go on a temporary AI binge, like trying to prove that insertion sort is correct. For that I need to do symbolic manipulation, and I usually fall back on Lisp.
Little domain-specific-languages. I know Lisp isn't really practical, but if I want to try out a little DSL without having to get all wrapped up in parsing, etc., Lisp macros make it easy.
Little play algorithms like minimax game tree search can be done in like three lines.
- Want to try lambda calculus? It's easy in Lisp.
Mainly what Lisp does for me is mental exercise. Then I can carry that over into more practical languages.
P.S. Speaking of lambda calculus, what also started in the 1970s, in that same AI millieu, was that OO started invading everybody's brain, and somehow, interest in what it is seems to have crowded out much interest in what it is good for. I.e. work on machine learning, natural language, vision, problem solving, all sort of went to the back of the room while classes, messages, types, polymorphism, etc. went to the front.
One thing I like is the fact that I can upgrade code "run-time" without losing application state. It's a thing only useful in some cases, but when it is useful, having it already there (or, for only a minimal cost during development) is MUCH cheaper than having to implement it from scratch. Especially since this comes at "no to almost no" cost.
Have you taken a look at this explanation of why macros are powerful and flexible? No examples in other languages though, sorry, but it might sell you on macros.
@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 :-)
One specific thing that impressed me is the ability to write your own object-oriented programming extension, if you happen not to like the included CLOS.
One of them is in Garnet, and one in Paul Graham's On Lisp.
There's also a package called Screamer that allows nondeterministic programming (which I haven't evaluated).
Any language that allows you to change it to support different programming paradigms has to be flexible.
You might find this post by Eric Normand helpful. He describes how as a codebase grows, Lisp helps by letting you build the language up to your application. While this often takes extra effort early on, it gives you a big advantage later.
The simple fact that it's a multi-paradigm language makes it very very flexible.
John Ousterhout made this interesting observation regarding Lisp in 1994:
Language designers love to argue about why this language or that language must be better or worse a priori, but none of these arguments really matter a lot. Ultimately all language issues get settled when users vote with their feet.
If [a language] makes people more productive then they will use it; when some other language comes along that is better (or if it is here already), then people will switch to that language. This is The Law, and it is good. The Law says to me that Scheme (or any other Lisp dialect) is probably not the "right" language: too many people have voted with their feet over the last 30 years.
http://www.vanderburg.org/OldPages/Tcl/war/0009.html
참고URL : https://stackoverflow.com/questions/106058/practical-example-of-lisps-flexibility
'Nice programing' 카테고리의 다른 글
| 스케일이 다른 matplotlib의 다중 축 (0) | 2020.11.09 |
|---|---|
| 커서를 특정 행과 열로 이동하려면 어떻게합니까? (0) | 2020.11.09 |
| PHP에서 파일 시스템 경로 문자열을 결합하는 방법은 무엇입니까? (0) | 2020.11.09 |
| 클래스가 키 값 코딩을 준수하지 않습니다. (0) | 2020.11.09 |
| 특정 좌표 근처의 특정 장소를 검색하는 Google지도 URL을 형성합니다. (0) | 2020.11.09 |