Professional Documents
Culture Documents
Author:CristianChidesa
Email:lecprog@gmail.com ,lecprog@yahoo.com
Inmyopinion,adisadvantageinCommonLispprogrammingisitsevaluation
mechanismwhichusesquote,backquoteandcommaoperators.
ThequoteisawayofprotectingLispexpressionsfromevaluationbut,ateach
evaluationstepofamulti-quotedexpression,aquoteisremovedandthe
expressiongloballychanges.
Sometimes,weneedtopreserveintactsomeexpressionsthroughsubsequent
evaluationsandquoteandbackquoteoperatorsdontofferagoodsolutionin
thiscase.
TheexpressionsinLecprog2arebuiltasinCommonLisp,everyexpression
iseitheranatom(string,number,constantsymbol)oralist.
ButunlikeLisp,Lecprog2doesn'tusequoteandbackquoteoperators.
InLecprog2 thereexisttwotypesoflists:listsdelimitedby(round)parenthesis
(i.e."("and")")andlistsdelimitedbysquarebrackets(i.e."["and"]").
Bothtypesoflistscancontainatomsandotherlists.
Listsdelimitedbyparenthesisareusedforfunctioncallswhilelistsdelimited
bysquarebracketsareusedtorepresentdata,functionparameters,(local)
variableswithinaniterativestructure"iter_sequence"andclausesofinstruction
"filter"(instruction"filter"issimilartoinstruction"cond"inCommonLisp).
Thereforealistcanrepresentagenerictreestructurecontainingfunctioncalls
mixedwithdata.
TheaxiomsforevaluatingexpressionsinLecprog2aregivenbelow(textin
bold + italic + green color)but,firstly,let'sseeafewexamplesinLecprog2:
Lecprog2doesn'thaveaspecialatom"nil".InLecprog2,insteadofatom"t"
thereexiststheatom"true".Lecprog2hasalsotheatom"false"whichis
differentfrom"[ ]".
In Lecprog2, an atom is evaluated to itself. We can attach a value to
an atom by using the function "set" but we need to use the function
"get_value" to retrieve the value attached to that atom.
Ex:
>>> (set a w) --> nothing
//i.e.noreturnvalueforfunction"set"
>>> a --> a//nodirectevaluationforanatom(!);needtousefunction"get_value"
>>> (get_value a) --> w
>>> (get_value 7) --> 7
//byconvention,"get_value"appliedtoaconstant
//returnsthatconstant
>>> (get_value "a string") --> "a string"
//thesameconvention
Fromnowon,forsimplicity,we'llwrite"^a"insteadof"(get_value a)",
"a"beinganatomorafunctionparameterora(local)variablewithinan
iterativestructure"iter_sequence".
>>> (set b c) --> nothing
>>> ^b --> c
>>> (set a ^b) --> nothing
>>> ^a --> c
Note:1)"nothing"and"error"arespecialatomsinLecprog2.
"error"isusedasresultofanevaluationthatgiveserror.
2)Anycallof"set"or"defun"functionsalwaysreturns"nothing"or
"error".
Listsdelimitedbysquarebracketsandcontainingonlyatomsexceptingthe
specialatom"nothing",arepuredataanddonotchangethroughevaluation
inLecprog2.
Ex:
>>> [a b c] --> [a b c]//nochanges
>>> [cons 2 [3 4]] --> [cons 2 [3 4]]//nochanges
The first item of a list delimited by (round) parenthesis (i.e."(" and ")")
must be a standard function, or an user-defined function or an anonymous
function (lambda expressions).
The evaluation of such list (delimited by "(" and ")") means to evaluate
the parameters of that function (which may entail recursive evaluation for
parameters that are lists) and then apply that function to those evaluated
parameters.
Evaluating a list delimited by square brackets (i.e. "[" and "]") means
to evaluate sequentially each item of that list (which implies recursive
evaluation if such item is also a list) and the result will be the list
containing these evaluated items.
Note: Thefirstitemofalistdelimitedby(round)parenthesisisnot
evaluated,onlysubsequentitemsfromthelist,i.e.theparametersof
thatfunctioncall,areevaluated(iftheyexist).
Ex:
>>> [ car [cdr [a b c d]] ] --> [car [cdr [a b c d]]]
//nochanges
>>> ( car [cdr [a b c d]] ) --> cdr
>>> [ car (cdr [a b c d]) ] --> [car [b c d]]
>>> ( car (cdr [a b c d]) ) --> b
>>> ( car [(cdr [x y z]) u v] ) --> (car [[y z] u v]) --> [y z]
//modifyitematindex:
>>> ( set_item 0 y [b c d] ) --> [y c d]
>>> ( set_item 2 y [b c d] ) --> [b c y]
>>> ( set_item 2 nothing [b c d] ) --> [b c]
>>> ( set_item 3 y [b c d] ) --> [b c d]//unchanged
>>> ( set_item 3 nothing [b c d] ) --> [b c d]//unchanged
//getitematindex:
>>> ( get_item 0 [[x u v] [b c] v] ) --> [x u v]
>>> ( get_item 1 [[x u v] [b c] v] ) --> [b c]
>>> ( get_item 3 [[x u v] [b c] v] ) --> nothing
//---------------------------------------------------------
>>> ( apply + [3 4 5] ) --> 12//thesameas:
//(+345)-->12
>>> ( apply cdr [[3 4 5]] ) --> [4 5]//thesameas:
//(cdr[345])-->[45]
>>> ( apply cons [a [b c]] ) --> [a b c] //thesameas:
//(consa[bc])-->[abc]
Afewmoreexampleswithatom"error":
>>> (+ 5 [a b]) --> error
//i.e.theresultistheatom"error"
>>> ( get_error_msg ) -->
--> "all the arguments of function '+' must be numbers"
TheevaluationruleinLecprog2shouldbesupplementedwithstatements
aboutatoms"error"and"nothing":
[ + 5 nothing 2 ] --> [ + 5 2 ]
[ nothing x nothing u w ] --> [x u w]
[ nothing nothing nothing ] --> [ ]
[ (set x 11) (set y 21) z ] --> [z]
[ (set x 11) (set y 21) ] --> [ ]
(car [nothing (+ 3 4)]) --> (car [7]) --> 7
(car [(set a 4) (* ^a 2)]) --> (car [8]) --> 8
Note:
1)Thespecialfunction"catch_error"willcatchtheerror,similarto
"try...catch..."inJava(C#,C++,etc),andcanbeusedtopreventthe
propagationofatom"error"tothetoplevelofthetreestructureexpression
duringevaluation.
Examples:
>>> [ 4 error x y ] --> [ 4 error x y ]
//nochanges
>>> [ 4 (3 2) x y ] --> [ 4 error x y ]
>>> ( + 5 (3 2) ) --> error
>>>
>>>
>>>
>>>
>>>
-->
error
Ex:
>>> (sum [1 2 7]) --> 10
//---------------------------------------------------------
SupposethatLecprog2hasastandardfunction"eq"whichteststheequality
oftwoatomsbutdoesn'tworkforlists:
>>> (eq a a) --> true
>>> (eq a b) --> false
>>> (eq [a b] [a b]) --> false
>>> (eq [ ] [ ]) --> true
>>> (eq <any_atom> <any_list>) --> false
>>> (eq <any_list> <any_atom>) --> false
Ex:
>>> (equal
[a [[b] c]] [a [[b] c]]) --> true
>>> (equal
[a [[b] c]] [a [b c]]) --> false
//---------------------------------------------------------
>>> (defun append [ l1 l2 ]
(filter
[ (is_empty_list l1)
l2 ]
[ default
(cons (car l1) (append (cdr l1) l2)) ]
)
) --> nothing
Ex:
>>> (append [a b] [x y z]) --> [a b x y z]
//---------------------------------------------------------
Parametersoffunctionscanbeintheirturnfunctions:
>>> ( defun f1 [ x ] (+ x 1) ) --> nothing
>>> ( defun q2 [ h u v ]
(* (apply h [u]) (apply h [v]))
) --> nothing
>>> (q2 f1 4 7) --> 40
//---------------------------------------------------------
Afewmoreexampleswithuser-definedfunctionsinLecprog2:
>>> ( defun is_member [ x lst ]
( filter
[ (is_empty_list lst)
false ]
[ (equal x (car lst))
true ]
[ default
(is_member x (cdr lst)) ]
)
) --> nothing
Ex:
>>> ( is_member
[a [b]]
[x y [a [b]] c d] ) --> true
>>> ( is_member
[a [b]]
[x y a [b] c d] ) --> false
//---------------------------------------------------------
>>> ( defun rec_reverse [ lstSource lstRev ]
( filter
[ (is_empty_list lstSource)
lstRev ]
[ default
(rec_reverse (cdr lstSource)
(cons (car lstSource) lstRev)
)
]
)
) --> nothing
Ex:
>>> ( reverse [a b c d] ) -> [d c b a]
Note: Functions"append","is_member","equal","rec_reverse"canbedefined
inLecprog2inthesamewayasinCommonLisp.
//---------------------------------------------------------
>>> ( defun eval_infix [ x ]
( if(is_atomic x)
// clause"if...then"
( filter
[ (is_reserved_word x)
error ] //like"car","cdr",etc.
[ (is_number ^x)
^x ]
[ default
error ]
)
//clause"else"
( apply
(get_item 1 x)
//i.e.thearithmeticoperator
[ (eval_infix (get_item 0 x))
(eval_infix (get_item 2 x)) ]
)
)
) --> nothing
Ex:
>>> (set a 4) --> nothing
>>> ( eval_infix [[a + 3] * [a - 3]] ) -->
--> (apply * [7 1]) --> 7
//---------------------------------------------------------
Letsconsiderthefunction:
>>> (defun f2 [ u x y ]
( filter
[ (< ^x ^y)
[ (> ^x ^y)
[ default
(if
)
]
)
) --> nothing
>>>
>>>
>>>
>>>
Inordertosimplifythebodyofthisfunction,wecoulduseaspecial
function,auto_evalwhichwouldallowadirectevaluationofthe
functionparameters(i.e.evaluatingwithoutusingget_valuefunction):
>>> (defun f2 [ u x y ]
(auto_eval [x y]
(filter
[ (< x y)
(- (sqr y) (sqr x)) ]
[ (> x y)
(- (sqr x) (sqr y)) ]
[ default
(if (< ^u 0)
[u (sqr x)]
[u (* 12 x)]
)
]
)
)
) --> nothing
Note:1)Instruction"auto_eval"isanexceptiontotheevaluationrule.
//---------------------------------------------------------
Twoimportantfunctionswhichcanevaluateexpressionswrittenas
puredata(only"["and"]"):
]
[ default
(eval_data_list
fn_lst
data) ]
)
) --> nothing
Ex:
>>> ( eval_in_depth [+ * -]
[* [+ 5 3] [- 7 4]] ) --> 24
>>> ( eval_in_depth [+ -]
[* [+ 5 3] [- 7 4]] ) --> [* 8 3]
Lecprog2needsalsoiterativestructuresbecausetherearealotoftasks
thatcanbebetterhandlediterativelythanrecursively,forexamplewhen
processingbiglists.
>>> ( iter_sequence [ x y ]
( set x 4 )
( set y 5 )
) --> nothing
>>> ( iter_sequence [ x y ]
( set x 4 )
( set y ^x )
( exit_sequence (* 3 ^y) )
) --> 12
>>> ( iter_sequence [ x y ]
( set x 8 )
( set y 3 )
( if (< ^x ^y)
( exit_sequence (- ^y ^x) )
( exit_sequence (- ^x ^y) )
)
( exit_sequence 100 ) //note:thisinstructionisneverevaluated
) --> 5
>>> ( iter_sequence [ x s ]
( set x 1 )
( set s 0 )
( do
( if (>= ^x 10) (exit_sequence ^s) )
( set s (+ ^s ^x) ) //i.e.s=s+x;
( set x (+ ^x 2) )
//i.e.x=x+2;
)
) --> 25//i.e.:1+3+5+7+9-->25
Note:Instructions"iter_sequence"and"do"areexceptionstotheevaluationrule.
//----------------------------------------------------------
Anonymousfunctions(lambdaexpressions)-exceptiontotheevaluationrule;
we'lldenotelambdaby"@"("@"isaspecialatom):
>>> (@ [u] (+ u 1)) --> (@ [u] (+ u 1)) //nochanges
>>> ( (@ [u] (+ u 1)) 4 ) --> 5
>>> ( defun
q2 [ h u v ]
(* (apply h [u])
--> nothing
>>> ( q2
--> ( *
(@ [u] (+ u 1))
( apply
( apply
) --> 10
>>>
>>>
>>>
>>>
>>>
(
(
(
(
is_list
(@
is_atomic
is_lambda
is_lambda
(apply
[v]))
4 ) -->
(@ [u] (+ u 1))
(@ [u] (+ u 1))
[x] (*
(@ [x]
(@ [x]
[@ [x]
[1] )
[4] )
( cons
(@ [x] (* 2 x))
["a string" some_atom
21]
) -->
[ (@ [x] (* 2 x))
>>> ( get_item
2
["a string"
"a string"
some_atom
(@ [x] (* 4 x))
21 ]
(@ [x] (+ x 1))
) -->
(@ [x] (+ x 1))
32]
Function"eq_lambda"teststheequalityoftwolambdaexpressions
afterrenamingboundvariables.
>>> ( eq_lambda
(@ [x] (* 2 x))
(@ [y] (* 2 y)) ) --> true
Reflection in Lecprog2
InthisversionofpdfdocaboutLecprog2,unliketheolderversions,
IusedotherprimitivesforreflectioninLecprog2whichfocusespecially
onlambdaexpressions(anonymousfunctions).
InordertoendowLecprog2withanefficientmechanismofreflection,
itwouldbeusefultohavethefollowingprimitives:
1)"get_lambda",whichgivesthedefinitionofausernamedfunctionas
alambdaexpression.
Ex:
>>> ( defun q2 [ h u v ]
(* (apply h [u]) (apply h [v]))
) --> nothing
2)"function_from_lambda",whichdefinesafunctionwhosenameisthe
firstparameterandwhoseexpressionisgivenbyalambdaexpressionas
secondparameter.Ex:
>>> ( function_from_lambda
g4
( @ [y] (if (< y 1) (- 1 y) (- y 1)) )
) -->nothing
//i.e.thedefinitionoffunction"g4"wasupdated
Note:Thisisequivalentwith:
>>> ( defun g4 [y]
(if (< y 1) (- 1 y) (- y 1)) ) --> nothing
3)Primitive"turn_lambda_into_data"turnsalambdaexpressionintodata.
Ittakesoneparameterwhichmustbealambdaexpression(i.e"(@ [...] ...)").
All"("willbereplacedwith"[# "andthecorresponding")"willbereplaced
with"]",butwithoneexception,"("and")"ofinternallambdaexpressions.
So,a"turn_lambda_into_data"callactsonlyontheenclosinglambda
expressionandonallinternalfunctioncallsexceptinginternallambda
expressions(ifthereexist).
Buttheexpressionresultingfromafirst"turn_lambda_into_data"call
canbeparsed(asbeingdata)andprimitive"turn_lambda_into_data"can
beusedagain,ifnecessary,inordertoturnanypossibleinternallambda
expressionintodata.
Ex:
>>> ( turn_lambda_into_data
(@ [x y] (+ (sqr x) (sqr y))) ) -->
--> [ # @ [_x21 _y22] [# + [# sqr _x21] [# sqr _y22]] ]
Note:"#"isaspecialatom.
>>> ( cons # [car [b c]] ) --> [# car [b c]]
>>> ( car [# + 3 4] ) --> #
>>> ( set # <some_expression> ) --> error
Anotherexample:
>>> ( turn_lambda_into_data
( @ [x]
( filter
[ (< x 1)
(@ [y] (* 2 y)) ]
[ (< x 3)
(iter_sequence [w]
(set w 7)
(exit_sequence ^w)
)
]
[ default
(@ [y] (* 5 y)) ]
)
)
) -->
--> [# @ [_x23]
[# filter
[ [# < _x23 1]
(@ [y] (* 2 y)) ]
[ [# < _x23 3]
[# iter_sequence [_w24]
[# set _w24 7]
[# exit_sequence [# get_value _w24]]
]
]
[ default
(@ [y] (* 5 y)) ]
]
]
2)Iftheparameterina"turn_lambda_into_data"callisalambdaexpression
havingparameterslikeintheexamplesabove,thenweneedtouseaspecial
typeofsymbolsinLecprog2,let'scallthemauxiliarysymbols(orunregistered
symbols,unlikeatomswhichareregisteredsymbolswithinLecprog2
environment).
Byconvention,thenameofanauxiliarysymbolbeginswith"_".
We'llsupposealsothatLecprog2environmentmanagesaglobalcounterwhich
isautomaticallyincrementedateachcalloffunction"turn_lambda_into_data"
foralambdaexpressionorateachcallofaprimitive"new_aux_symb".
Thelambdaexpression'sparametersandlocalvariablesoftheenclosinglambda
expressionwillberenamedtoauxiliarysymbolsbyusingnewvaluesofthiscounter.
Intheexamplesabove,theglobalcountervalueforauxiliarysymbolswas20
beforethefirst"turn_lambda_into_data"callandbecame24afterthesecondcall.
Inthefirstexample,parameter"x"wasreplacedwiththeauxiliarysymbol"_x21"
andparameter"y"wasreplacedwiththeauxiliarysymbol"_y22".
Inthesecondexample,parameter"x"wasreplacedwiththeauxiliarysymbol
"_x23"andlocalvariable"w"ofinstruction"iter_sequence"wasreplacedwiththe
auxiliarysymbol"_w24".
Newauxiliarysymbolscanalsobegeneratedbyusingprimitive"new_aux_symb".
Ex:
>>> ( set atom1 (new_aux_symb "aux") ) --> nothing
>>> ^atom1 --> _aux25
//theglobalcountervalueis25now
Byconvention,anauxiliarysymbolwillbehavelikeanatomexceptingwhen
itisusedwithfunctions"set"and"get_value":
>>> ( cons _aux31 [a b c] ) --> [_aux31 a b c]
>>> ( get_item 1 [b _aux17 "some string" _aux5] ) --> _aux17
>>> ( is_atomic _aux31 ) --> true
>>> ( is_aux_symb _aux31 ) --> true
>>> ( is_aux_symb [a b c] ) --> false
>>> ( is_aux_symb "a string" ) --> false
>>> ( set _aux31 "some string as value" ) --> error
>>> ( get_error_msg ) -->
--> "the first parameter of function 'set' cannot be
an auxiliary symbol"
>>> ( get_value _aux31 ) --> error
>>> ( get_error_msg ) -->
--> "the parameter of function 'get_value' cannot be
an auxiliary symbol"
//---------------------------------
4)Primitive"get_lambda_from_data"constructsalambdaexpressionfromdata.
Ittakestwoparameters:
-alistofauxiliarysymbols(whichcanbeanemptydatalist)representingthe
futurelambdaexpression'sparameterlist;
-anexpressionrepresentingthefuturelambdaexpression'sbody.
"get_lambda_from_data"replaceswithinitssecondparameter(i.e.future
lambdaexpression'sbody)allthe"[#"with"("andthecorresponding"]"with")",
exceptinginsideanyexistinginternallambdaexpressions(i.e.existinginternal
lambdaexpressionsareignored).
Allthesereplacements(i.e"[# ...]"<--"( ... )")aremadewithoutevaluation.
If"_x"isanauxiliarysymboloccurringinboth(future)lambdaexpression's
parameterlistand(future)lambdaexpression'sbodythen"_x"willbereplaced
with"x"insidebothofthem;afterwards,alltheoccurrencesof"x"frominside
thelambdaexpression'sbodywillbeboundtoparameter"x"(fromlambda
expression'sparameterlist).
Thus,theresultofa"get_lambda_from_data"callwillbealambdaexpression
constructedinthisway.
Ex:
>>> ( get_lambda_from_data
[]
[atom1 "a string"
--> ( @ [] [atom1 "a string" 12] )
>>> ( get_lambda_from_data
[_y _z]
[# + [# sqr _y] [# sqr _z]]
) -->
( @ [y z] (+ (sqr y) (sqr z)) )
Anotherexample:
>>> ( get_lambda_from_data
12] ) -->
[_x]
[# filter
[ [# < _x 1]
(@ [y] (* 2 y)) ]
[ [# < _x 3]
[# iter_sequence [_w]
[# set _w 7]
[# exit_sequence [#
]
]
[ default
(@ [y] (* 5 y)) ]
]
get_value
_w]]
) -->
(@
[x]
( filter
[ (< x 1)
(@ [y] (* 2 y)) ]
[ (< x 3)
(iter_sequence [w]
(set w 7)
(exit_sequence ^w)
)
]
[ default
(@ [y] (* 5 y)) ]
)
)
//---------------------------------
Let'ssupposethatweobtainedprogrammaticallyinLecprog2an"iter_sequence"
expressionrepresentedaspuredataandwewanttorunthissourcecode.
Firstly,we'lluseprimitive"get_lambda_from_data"inordertocreate
alambdaexpressionwhosebodyisexactlythat"iter_sequence"expression.
Thelambdaexpression'sparameterlistwillbeempty.
Forconvenience,wecanstorethatlambdaexpressionasvalueofanatom
"atom_for_lambda"andthen,we'llmakeacallofthatlambdaexpression
byusingfunction"apply":
( get_lambda_from_data
[]
[#
]
)
//i.e.noparametersforthefuturelambdaexpression
iter_sequence [_x]
[# set _x 25]
[# exit_sequence
[# (@ [y] (* 2 ^y))
//endof"[#iter_sequence ..."
)
--> nothing
>>> ( apply
--> (apply
(@
^atom_for_lambda
[] ) -->
[]
( iter_sequence [ x ]
(set x 25)
( exit_sequence ((@ [y] (* 2 ^y)) x) )
)
)
[]
) -->
(* 2 25) --> 50
//---------------------------------------------------------
Remark:
Obviously,wecanbuildprogrammaticallyatruntimeexpressionscontaining
onlyatomsandlistsenclosedbysquarebrackets(i.e"["and"]")whichmay
possiblystartwith"#"(i.e.puredata).
So,theuseofprimitives"get_lambda","turn_lambda_into_data",
"new_aux_symb","get_lambda_from_data","function_from_lambda"
couldallowustoprogrammaticallydefinenewfunctionsatruntime,towrite
functionswhichmodifyotherfunctionsoreventowritefunctionswhich
modifythemselvesatruntime.
ThisfeaturecouldmakeLecprog2reallysuitableforMachineLearning,
ConstraintProgrammingandotherbranchesofArtificialIntelligence.
Conclusion:
Inmyopinion,anewprogramminglanguagewouldbereallyusefulifthat
languagecouldofferamaximumofflexibilityandpowerforprogramming
byusingaminimalsetofconventionsandprogrammingrules.
IintendtodefinetheaxiomsofLecprog2followingthisprinciple.
_x] ]