-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathCodePatterns.html
4354 lines (3307 loc) · 150 KB
/
CodePatterns.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="author" content="Lutz Mueller"/>
<meta name="keywords"
content="newLISP LISP SCHEME programming language manual reference Artificial Intelligence AI NUEVATEC"/>
<meta name="description" content="newLISP Code Patterns"/>
<title>newLISP Code Patterns</title>
<style type="text/css" media="screen">
<!--
.title {
font-family:Optima, Georgia, Times New Roman, Times, serif;
font-size:300%;
font-weight: 500;
color: #404040;
}
body, h4, p {
font-family: Lucida Sans, Helvetica, sans-serif;
line-height: 120%;
color: #404040;
}
h1, h2, h3 {
margin-top: 3%;
font-family: Lucida Sans, Helvetica, sans-serif;
line-height: 120%;
font-weight: 300;
color: #202020;
}
table {
margin: 0px;
margin-left: 10px;
margin-right: 10px;
border-style: solid;
border-width: 0px;
border-color: #888888;
padding: 3px;
background: #f8ffff;
font-size: 95%;
}
th {
border-style: solid;
border-width: 1px;
border-color: #888888;
padding: 3px;
background: #eeeeee;
font-size: 100%;
}
td {
border-style: solid;
border-width: 1px;
border-color: #888888;
padding: 3px;
background: #f8ffff;
font-size: 100%;
}
pre {
margin: 0px;
margin-left: 10px;
margin-right: 10px;
border-style: dashed;
border-width: 1px;
border-color: #888888;
padding: 4px;
font-family: Andale Mono, "Bitstream Vera Sans Mono", Monaco, "Courier New";
font-size: 90%;
background: #f8ffff;
}
tt {
font-family: Andale Mono, "Bitstream Vera Sans Mono", Monaco, "Courier New";
font-size: 100%;
}
-->
</style>
</head>
<body style="margin: 20px;" text="#000000" bgcolor="#FFFFFF" link="#376590" vlink="#551A8B" alink="#ffAA28">
<br/><br/>
<center>
<h1 class="title">Code Patterns in newLISP<font size="-1">®</font>
</h1>Version 2018 July 12<sup>th</sup><br/>
<a href="http://newlisp.org">newLISP</a> v.10.7.4
</center>
<br/><br/><br/>
<center>
<span style="line-height:80%;">
<font size="-2">
Copyright © 2015 Lutz Mueller, <a href="http://www.nuevatec.com">www.nuevatec.com</a>. All rights reserved.<br/>
Permission is granted to copy, distribute and/or modify this document under the terms of the <a href="#GFDL">GNU Free Documentation License</a>,<br/>
Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts,<br/>
and no Back-Cover Texts. A copy of the license is included in the section entitled GNU Free Documentation License.<br/>
<br/>
newLISP is a registered trademark of Lutz Mueller.
</font>
</span>
</center>
<ul>
<li><a href="#toc-1">1. Introduction</a>
</li>
<li><a href="#toc-2">2. newLISP script files</a>
<ul>
<li>Command line options</li>
<li>Scripts as pipes</li>
<li>File filters</li>
</ul></li>
<li><a href="#toc-3">3. Writing software in modules</a>
<ul>
<li>Structuring an application</li>
<li>More than one context per file</li>
<li>The default function</li>
<li>Packaging data with contexts</li>
<li>Passing objects by reference</li>
</ul></li>
<li><a href="#toc-4">4. Local variables</a>
<ul>
<li>Locals in looping functions</li>
<li>Locals in <tt>let</tt>, <tt>letn</tt>, <tt>local</tt> and <tt>letex</tt></li>
<li>Unused parameters as locals</li>
<li>Default variable values</li>
<li><tt>args</tt> as local substitute</li>
<li><tt>args</tt> and <tt>local</tt> used together for named variables</li>
</ul></li>
<li><a href="#toc-5">5. Walking through lists and data</a>
<ul>
<li>Recursion or iteration?</li>
<li>Speed up with memoization</li>
<li>Walking a tree</li>
<li>Walking a directory tree</li>
</ul></li>
<li><a href="#toc-6">6. Modifying and searching lists</a>
<ul>
<li><tt>push</tt> and <tt>pop</tt></li>
<li>Extend using <tt>extend</tt></li>
<li>Accessing lists</li>
<li>Selecting more elements</li>
<li>Filtering and differencing lists</li>
<li>Changing list elements</li>
<li>The anaphoric variable</li>
<li>Replace in simple lists</li>
<li>Replace in nested lists</li>
<li>Passing lists by reference</li>
<li>Variable expansion</li>
<li>Destructuring nested lists</li>
</ul></li>
<li><a href="#toc-7">7. Program flow</a>
<ul>
<li>Loops</li>
<li>Blocks</li>
<li>Branching</li>
<li>Fuzzy flow</li>
<li>Flow with <tt>catch</tt> and <tt>throw</tt></li>
<li>Leave loops with a break condition</li>
<li>Change flow with <tt>and</tt> or <tt>or</tt></li>
</ul></li>
<li><a href="#toc-8">8. Error handling</a>
<ul>
<li>newLISP errors</li>
<li>User defined errors</li>
<li>Error event handlers</li>
<li>Catching errors</li>
<li>Operating system errors</li>
</ul></li>
<li><a href="#toc-9">9. Functions as data</a>
<ul>
<li>Manipulate after definition</li>
<li>Mapping and applying</li>
<li>Functions making functions</li>
<li>Functions with memory</li>
<li>Functions using self modifying code</li>
</ul></li>
<li><a href="#toc-10">10. Text processing</a>
<ul>
<li>Regular expressions</li>
<li>Scanning text</li>
<li>Appending strings</li>
<li>Growing strings in place</li>
<li>Rearranging strings</li>
<li>Modifying strings</li>
</ul></li>
<li><a href="#toc-11">11. Dictionaries and hashes</a>
<ul>
<li>Hash-like key → value access</li>
<li>Saving and loading dictionaries</li>
</ul></li>
<li><a href="#toc-12">12. TCP/IP client server</a>
<ul>
<li>Open connection</li>
<li>Closed transaction</li>
</ul></li>
<li><a href="#toc-13">13. UDP communications</a>
<ul>
<li>Open connection</li>
<li>Closed transaction</li>
<li>Multi-cast communications</li>
</ul></li>
<li><a href="#toc-14">14. Non-blocking communications</a>
<ul>
<li>Using <tt>net-select</tt></li>
<li>Using <tt>net-peek</tt></li>
</ul></li>
<li><a href="#toc-15">15. Controlling other applications</a>
<ul>
<li>Using <tt>exec</tt></li>
<li>STD I/O pipes</li>
<li>Communicate via TCP/IP</li>
<li>Communicate via named FIFO</li>
<li>Communicate via UDP</li>
</ul></li>
<li><a href="#toc-16">16. Launching apps blocking</a>
<ul>
<li>Shell execution</li>
<li>Capturing std-out</li>
<li>Feeding std-in</li>
</ul></li>
<li><a href="#toc-17">17. Semaphores, shared memory</a>
</li>
<li><a href="#toc-18">18. Multiprocessing and Cilk</a>
<ul>
<li>Starting concurrent processes</li>
<li>Watching progress</li>
<li>Invoking spawn recursively</li>
<li>Event driven notification</li>
</ul></li>
<li><a href="#toc-19">19. Message exchange</a>
<ul>
<li>Blocking message sending and receiving</li>
<li>Blocking message exchange</li>
<li>Non blocking message exchange</li>
<li>Message timeouts</li>
<li>Evaluating messages</li>
<li>Acting as a proxy</li>
</ul></li>
<li><a href="#toc-20">20. Databases and lookup tables</a>
<ul>
<li>Association lists</li>
<li>Nested associations</li>
<li>Updating nested associations</li>
<li>Combining associations and hashes</li>
</ul></li>
<li><a href="#toc-21">21. Distributed computing</a>
<ul>
<li>Setting up in server mode</li>
<li>Start a state-full server</li>
<li>Stateless server with inetd</li>
<li>Test the server with telnet</li>
<li>Test with netcat on Unix</li>
<li>Test from the command line</li>
<li>Test HTTP with a browser</li>
<li>Evaluating remotely</li>
<li>Setting up the <tt>net-eval</tt> parameter structure</li>
<li>Transferring files</li>
<li>Loading and saving data</li>
<li>Local domain Unix sockets</li>
</ul></li>
<li><a href="#toc-22">22. HTTPD web server only mode</a>
<ul>
<li>Environment variables</li>
<li>Pre-processing the request</li>
<li>CGI processing in HTTP mode</li>
<li>Media types in HTTP modes</li>
</ul></li>
<li><a href="#toc-23">23. Extending newLISP</a>
<ul>
<li>Simple versus extended FFI interface</li>
<li>A shared library in C</li>
<li>Compile on Unix</li>
<li>Compile a DLL on Win32</li>
<li>Importing data structures</li>
<li>Memory management</li>
<li>Unevenly aligned structures</li>
<li>Passing parameters</li>
<li>Extracting return values</li>
<li>Writing library wrappers</li>
<li>Registering callbacks in external libraries</li>
</ul></li>
<li><a href="#toc-24">24. newLISP as a shared library</a>
<ul>
<li>Evaluating code in the shared library</li>
<li>Registering callbacks</li>
</ul></li>
</ul>
<br/><br/>
<br/><center>§</center><br/>
<a name="toc-1"></a>
<h2>1. Introduction</h2>
<p>When programming in newLISP, certain functions and usage patterns occur repeatedly. For some problems, an optimal way to solve them evolves over time. The following chapters present example code and explanations for the solution of specific problems when programming in newLISP.</p>
<p>Some content is overlapping with material covered in the newLISP Users Manual and Reference or presented here with a different slant.</p>
<p>Only a subset of newLISP's total function repertoire is used here. Some functions demonstrated have additional calling patterns or applications not mentioned on these pages.</p>
<p>This collection of patterns and solutions is a work in progress. Over time, material will be added or existing material improved.</p>
<br/><center>§</center><br/>
<a name="toc-2"></a>
<h2>2. newLISP script files</h2>
<h3>Command line options</h3>
<p>On Linux/Unix, put the following in the first line of the script/program file:</p>
<pre>
#!/usr/bin/newlisp
</pre>
<p>specifying a bigger stack:</p>
<pre>
#!/usr/bin/newlisp -s 100000
</pre>
<p>or</p>
<pre>
#!/usr/bin/newlisp -s100000
</pre>
<p>Operating systems' shells behave differently when parsing the first line and extracting parameters. newLISP takes both attached or detached parameters. Put the following lines in small script to test the behavior of the underlying OS and platform. The script changes the stack size allocated to 100,000 and limits newLISP cell memory to about 10 M bytes.</p>
<pre>
#!/usr/bin/newlisp -s 100000 -m 10
(println (main-args))
(println (sys-info))
(exit) ; important
</pre>
<p>A typical output executing the script from the system shell would be:</p>
<pre>
./arg-test
("/usr/bin/newlisp" "-s" "100000" "-m" "10" "./arg-test")
(308 655360 299 2 0 100000 8410 2)
</pre>
<p>Note that few programs in newLISP need a bigger stack configured; most programs run on the internal default of 2048. Each stack position takes an average of 80 bytes. Other options are available to start newLISP. See the Users Manual for details.</p>
<h3>Scripts as pipes</h3>
<p>The following example shows how a file can be piped into a newLISP script. </p>
<pre>
#!/usr/bin/newlisp
#
# uppercase - demo filter script as pipe
#
# usage:
# ./uppercase < file-spec
#
# example:
# ./uppercase < my-text
#
#
(while (read-line) (println (upper-case (current-line))))
(exit)
</pre>
<p>The file will be printed to <tt>std-out</tt> translated to uppercase.</p>
<p>The following program would also work with binary non-textual information
containing <tt>0</tt>'s :</p>
<pre>
#!/usr/bin/newlisp
;
; inout - demo binary pipe
;
; read from stdin into buffer
; then write to stdout
;
; usage: ./inout < inputfile > outputfile
;
(while (read 0 buffer 1024)
(write 1 buffer 1024))
(exit)
</pre>
<p>Set buffersize to best performance.</p>
<h3>File filters</h3>
<p>The following script works like a Unix <tt>grep</tt> utility iterating through files and filtering each line in a file using a regular expression pattern.</p>
<pre>
#!/usr/bin/newlisp
#
# nlgrep - grep utility on newLISP
#
# usage:
# ./nlgrep "regex-pattern" file-spec
#
# file spec can contain globbing characters
#
# example:
# ./nlgrep "this|that" *.c
#
# will print all lines containing 'this' or 'that' in *.c files
#
(dolist (fname (3 (main-args)))
(set 'file (open fname "read"))
(println "file ---> " fname)
(while (read-line file)
(if (find (main-args 2) (current-line) 0)
(write-line)))
(close file))
(exit)
</pre>
<p>The expression:</p>
<pre>
(3 (main-args))
</pre>
<p>is a short form of writing:</p>
<pre>
(rest (rest (rest (main-args))))
</pre>
<p>It returns a list of all the filenames. This form of specifying indexes for rest is called implicit indexing. See the Users Manual for implicit indexing with other functions. The expression <tt>(main-args 2)</tt> extracts the 3rd argument from the command line containing the regular expression pattern.</p>
<h3>newLISP as a pipe</h3>
<p>Pipe one-liners directly into the executable for evaluation of short expressions:</p>
<pre>
~> echo '(+ 1 2 3)' | newlisp
6
~>
</pre>
<br/><center>§</center><br/>
<a name="toc-3"></a>
<h2>3. Writing software in modules</h2>
<h3>Structuring an application</h3>
<p>When writing bigger applications or when several programmers are working on the same code base, it is necessary to divide the code base into modules. Modules in newLISP are implemented using contexts, which are namespaces. Namespaces allow lexical isolation between modules. Variables of the same name in one module cannot clash with variables of the same name in another module.</p>
<p>Typically, modules are organized in one context per file. One file module may contain database access routines.</p>
<pre>
; database.lsp
;
(context 'db)
(define (update x y z)
...
)
(define (erase x y z)
...
)
</pre>
<p>Another module may contain various utilities</p>
<pre>
; auxiliary.lsp
;
(context 'aux)
(define (getval a b)
...
)
</pre>
<p>Typically, there will be one MAIN module that loads and controls all others:</p>
<pre>
; application.lsp
;
(load "auxiliary.lsp")
(load "database.lsp")
(define (run)
(db:update ....)
(aux:putval ...)
...
...
)
(run)
</pre>
<h3>More than one context per file</h3>
<p>When using more than one context per file, each context section should be closed with a <tt>(context MAIN)</tt> statement:</p>
<pre>
; myapp.lsp
;
(context 'A)
(define (foo ...) ...)
(context MAIN)
(context 'B)
(define (bar ...) ...)
(context MAIN)
(define (main-func)
(A:foo ...)
(B:bar ...)
)
</pre>
<p>Note that in the namespace statements for contexts <tt>A</tt> and <tt>B</tt> that the context names are quoted because they are newly created, but <tt>MAIN</tt> can stay unquoted because it already exists when newLISP starts up. However, quoting it does not present a problem.</p>
<p>The line <tt>(context MAIN)</tt> that closes a context can be omitted by using the following technique:</p>
<pre>
; myapp.lsp
;
(context 'A)
(define (foo ...) ...)
(context 'MAIN:B)
(define (bar ...) ...)
(context 'MAIN)
(define (main-func)
(A:foo ...)
(B:bar ...)
)
</pre>
<p>The line <tt>(context 'MAIN:B)</tt> switches back to MAIN then opens the new context <tt>B</tt>.</p>
<h3>The default function</h3>
<p>A function in a context may have the same name as the host context itself. This function has special characteristics:</p>
<pre>
(context 'foo)
(define (foo:foo a b c)
...
)
</pre>
<p>The function <tt>foo:foo</tt> is called the <tt>default function</tt>, because when using the context name <tt>foo</tt> like a function, it will default to <tt>foo:foo</tt></p>
<pre>
(foo x y z)
; same as
(foo:foo x y z)
</pre>
<p>The default function makes it possible to write functions which look like normal functions but carry their own lexical namespace. We can use this to write functions which keep state:</p>
<pre>
(context 'generator)
(define (generator:generator)
(inc acc)) ; when acc is nil, assumes 0
(context MAIN)
(generator) → 1
(generator) → 2
(generator) → 3
</pre>
<p>The following is a more complex example for a function generating a Fibonacci sequence:</p>
<pre>
(define (fibo:fibo)
(if (not fibo:mem) (set 'fibo:mem '(0 1)))
(last (push (+ (fibo:mem -1) (fibo:mem -2)) fibo:mem -1)))
(fibo) → 1
(fibo) → 2
(fibo) → 3
(fibo) → 5
(fibo) → 8
...
</pre>
<p>This example also shows how a default function is defined <tt>on-the-fly</tt> without the need of explicit <tt>context</tt> statements. As an alternative, the function could also have been written so that the context is created explicitly:</p>
<pre>
(context 'fibo)
(define (fibo:fibo)
(if (not mem) (set 'mem '(0 1)))
(last (push (+ (mem -1) (mem -2)) mem -1)))
(context MAIN)
(fibo) → 1
(fibo) → 2
(fibo) → 3
(fibo) → 5
(fibo) → 8
</pre>
<p>Although the first form is shorter, the second form is more readable.</p>
<h3>Packaging data with contexts</h3>
<p>The previous examples already presented functions packaged with data in a namespace. In the <tt>generator</tt> example the <tt>acc</tt> variable kept state. In the <tt>fibo</tt> example the variable <tt>mem</tt> kept a growing list. In both cases, functions and data are living together in a namespace. The following example shows how a namespace holds only data in a default functor:</p>
<pre>
(set 'db:db '(a "b" (c d) 1 2 3 x y z))
</pre>
<p>Just like we used the default function to refer to <tt>fibo</tt> and <tt>generator</tt> we can refer to the list in <tt>db:db</tt> by only using <tt>db</tt>. This will work in all situations where we do list indexing:</p>
<pre>
(db 0) → a
(db 1) → "b"
(db 2 1) → d
(db -1) → z
(db -3) → x
(3 db) → (1 2 3 x y z)
(2 1 db) → ((c d))
(-6 2 db) → (1 2)
</pre>
<h3>Passing objects by reference</h3>
<p>When the default functor is used as an argument in a user defined function, the default functor is passed by reference. This means that a reference to the original contents is passed, not a copy of the list or string. This is useful when handling large lists or strings:</p>
<pre>
(define (update data idx expr)
(if (not (or (lambda? expr) (primitive? expr)))
(setf (data idx) expr)
(setf (data idx) (expr $it))))
(update db 0 99) → a
db:db → (99 "b" (c d) 1 2 3 x y z)
(update db 1 upper-case) → "b"
db:db → (99 "B" (c d) 1 2 3 x y z)
(update db 4 (fn (x) (mul 1.1 x))) →
db:db → (99 "B" (c d) 1 2.2 3 x y z)
</pre>
<p>The data in <tt>db:db</tt> is passed via the <tt>update</tt> function parameter <tt>data</tt>, which now holds a reference to the context <tt>db</tt>. The <tt>expr</tt> parameter passed is checked to determine if it is a built-in function, operator or a user defined lambda expression and then works on <tt>$it</tt>, the anaphoric system variable containing the old content referenced by <tt>(data idx)</tt>.</p>
<p>Whenever a function in newLISP asks for a string or list in a parameter, a default functor can be passed by its context symbol. Another example:</p>
<pre>
(define (pop-last data)
(pop data -1))
(pop-last db) → z
db:db → (99 "B" (c d) 1 2.2 3 x y)
</pre>
<p>The function <tt>update</tt> is also a good example of how to pass operators or functions as a function argument (<tt>upper-case</tt> working on <tt>$it</tt>). Read more about this in the chapter <a href="#toc-9">Functions as data</a>.</p>
<br/><center>§</center><br/>
<a name="toc-4"></a>
<h2>4. Local variables</h2>
<h3>Locals in looping functions</h3>
<p>All looping functions like <tt>doargs</tt>, <tt>dolist</tt>, <tt>dostring</tt>, <tt>dotimes</tt>, <tt>dotree</tt> and <tt>for</tt> use local variables. During loop execution, the variable takes different values. But after leaving the looping function, the variable regains its old value. <tt>let</tt>, <tt>define</tt>, and <tt>lambda</tt> expressions are another method for making variables local:</p>
<h3>Locals in <tt>let</tt>, <tt>letn</tt>, <tt>local</tt> and <tt>letex</tt></h3>
<p><tt>let</tt> is the usual way in newLISP to declare symbols as local to a block.</p>
<pre>
(define (sum-sq a b)
(let ((x (* a a)) (y (* b b)))
(+ x y)))
(sum-sq 3 4) → 25
; alternative syntax
(define (sum-sq a b)
(let (x (* a a) y (* b b))
(+ x y)))
</pre>
<p>The variables x and y are initialized, then the expression (+ x y) is evaluated. The <tt>let</tt> form is just an optimized version and syntactic convenience for writing:</p>
<pre>
((lambda (sym1 [sym2 ...]) exp-body ) exp-init1 [ exp-init2 ...])
</pre>
<p>When initializing several parameters, a nested <tt>let</tt>, <tt>letn</tt> can be used to reference previously initialized variables in subsequent initializer expressions:</p>
<pre>
(letn ((x 1) (y (+ x 1)))
(list x y)) → (1 2)
</pre>
<p><tt>local</tt> works the same way but variables are initialized to <tt>nil</tt></p>
<pre>
(local (a b c)
... ; expressions using the locale variables a b c
)
</pre>
<p><tt>letex</tt> works similar to <tt>let</tt> but variables are expanded in the body to
values assigned.</p>
<pre>
; assign to local variable and expand in body
(letex ( (x 1) (y '(a b c)) (z "hello") ) '(x y z))
→ (1 (a b c) "hello")
; as in let, parentheses around the initializers can be omitted
(letex (x 1 y 2 z 3) '(x y z)) → (1 2 3)
</pre>
<p>After exiting any of the <tt>let</tt>, <tt>letn</tt>, <tt>local</tt> or <tt>letex</tt>
expressions, the variable symbols used as locals get their old values back.</p>
<h3>Unused parameters as locals</h3>
<p>In newLISP, all parameters in user defined functions are optional. Unused parameters are filled with <tt>nil</tt> and are of local scope to the dynamic scope of the function. Defining a user function with more parameters than required is a convenient method to create local variable symbols:</p>
<pre>
(define (sum-sq a b , x y)
(set 'x (* a a))
(set 'y (* b b))
(+ x y))
</pre>
<p>The comma is not a special syntax feature but only a visual helper to separate normal parameters from local variable symbols. (Technically, the comma, like x and y, is a local variable and is set to nil.)</p>
<h3>Default variable values</h3>
<p>In the definition of a function default values can be specified:</p>
<pre>
(define (foo (a 1) (b 2))
(list a b))
(foo) → (1 2)
(foo 3) → (3 2)
(foo 3 4) → (3 4)
</pre>
<h3><tt>args</tt> as local substitute</h3>
<p>Using the <tt>args</tt> function no parameter symbols need to be used at all and <tt>args</tt> returns a list of all parameters passed but not taken by declared parameters:</p>
<pre>
(define (foo)
(args))
(foo 1 2 3) → (1 2 3)
(define (foo a b)
(args))
(foo 1 2 3 4 5) → (3 4 5)
</pre>
<p>The second example shows how <tt>args</tt> only contains the list of arguments not bound by the variable symbols <tt>a</tt> and <tt>b</tt>.</p>
<p>Indices can be used to access members of the <tt>(args)</tt> list:</p>
<pre>
(define (foo)
(+ (args 0) (args 1)))
(foo 3 4) → 7
</pre>
<h3><tt>args</tt> and <tt>local</tt> used together for named variables</h3>
<pre>
(define-macro (foo)
(local (len width height)
(bind (args) true)
(println "len:" len " width:" width " height:" height)
))
(foo (width 20) (height 30) (len 10))
<b>len:10 width:20 height:30</b>
</pre>
<p><tt>local</tt> will shadow / protect the values of the variables <tt>len</tt>, <tt>width</tt>
and <tt>height</tt> at higher dynamic scoping levels.</p>
<br/><center>§</center><br/>
<a name="toc-5"></a>
<h2>5. Walking through lists and data</h2>
<h3>Recursion or iteration?</h3>
<p>Although recursion is a powerful feature to express many algorithms in a readable form, it can also be inefficient in some instances. newLISP has many iterative constructs and high level functions like <tt>flat</tt> or the built-in XML functions, which use recursion internally. In many cases this makes defining a recursive algorithm unnecessary.</p>
<p>Some times a non-recursive solution can be much faster and lighter on system resources.</p>
<pre>
; classic recursion
; slow and resource hungry
(define (fib n)
(if (< n 2) 1
(+ (fib (- n 1))
(fib (- n 2)))))
</pre>
<p>The recursive solution is slow because of the frequent calling overhead. Also, the recursive solution uses a lot of memory for holding intermediate and frequently redundant results.</p>
<pre>
; iteration
; fast and also returns the whole list
(define (fibo n , f)
(set 'f '(1 0))
(dotimes (i n)
(push (+ (f 0) (f 1)) f)) )
</pre>
<p>The iterative solution is fast and uses very little memory.</p>
<h3>Speed up with memoization</h3>
<p>A memoizing function caches results for faster retrieval when called with the same parameters again. The following function makes a memoizing function from any built-in or user defined function with an arbitrary number of arguments. A namespace is created for the memoizing function as a data cache.</p>
<pre>
; speed up a recursive function using memoization
(define-macro (memoize mem-func func)
(set (sym mem-func mem-func)
(letex (f func c mem-func)
(lambda ()
(or (context c (string (args)))
(context c (string (args)) (apply f (args))))))))
(define (fibo n)
(if (< n 2) 1
(+ (fibo (- n 1))
(fibo (- n 2)))))
(memoize fibo-m fibo)
(time (fibo-m 25)) → 148
(time (fibo-m 25)) → 0
</pre>
<p>The function creates a context and <tt>default</tt> function for the original function with a new name and stores all results in symbols in the same context.</p>
<p>When memoizing recursive functions, include the raw lambda specification of the function so recursive calls are memoized too:</p>
<pre>
(memoize fibo
(lambda (n)
(if(< n 2) 1
(+ (fibo (- n 1))
(fibo (- n 2))))))
(time (fibo 100)) → 1
(fibo 80) → 37889062373143906
</pre>
<p>The <tt>fibo</tt> function in the last example would take hours to calculate without memoization. The memoized version takes only about a milli-second for an argument of <tt>100</tt>.</p>
<h3>Walking a tree</h3>
<p>Tree walks are a typical pattern in traditional LISP and in newLISP as well for walking through a nested list. But many times a tree walk is only used to iterate through all elements of an existing tree or nested list. In this case the built-in <tt>flat</tt> function is much faster than using recursion:</p>
<pre>
(set 'L '(a b c (d e (f g) h i) j k))
; classic car/cdr and recursion
;
(define (walk-tree tree)
(cond ((= tree '()) true)
((atom? (first tree))
(println (first tree))
(walk-tree (rest tree)))
(true
(walk-tree (first tree))
(walk-tree (rest tree)))))
; classic recursion
; 3 times faster
;
(define (walk-tree tree)
(dolist (elmnt tree)
(if (list? elmnt)
(walk-tree elmnt)
(println elmnt))))
(walk-tree L) →
a
b
c
d
e
...
</pre>
<p>Using the built-in <tt>flat</tt> in newLISP a nested list can be transformed into a flat list. Now the list can be processed with a <tt>dolist</tt> or <tt>map</tt>:</p>
<pre>
; fast and short using 'flat'
; 30 times faster with map
;
(map println (flat L))
; same as
(dolist (item (flat L)) (println item))
</pre>
<h3>Walking a directory tree</h3>
<p>Walking a directory tree is a task where recursion works well:</p>
<pre>
; walks a disk directory and prints all path-file names
;
(define (show-tree dir)
(when (directory? dir)
(dolist (nde (directory dir))
(if (and (directory? (append dir "/" nde))
(!= nde ".") (!= nde ".."))
(show-tree (append dir "/" nde))
(println (append dir "/" nde))))))
</pre>
<p>In this example recursion is the only solution, because the entire nested list of files is not available when the function is called but gets created recursively during function execution.</p>
<br/><center>§</center><br/>
<a name="toc-6"></a>
<h2>6. Modifying and searching lists</h2>
<p>newLISP has facilities for multidimensional indexing into nested lists. There are <tt>destructive</tt> functions like <tt>push</tt>, <tt>pop</tt>, <tt>setf</tt>, <tt>set-ref</tt>, <tt>set-ref-all</tt>, <tt>sort</tt> and <tt>reverse</tt> and many others for <tt>non-destructive</tt> operations, like <tt>nth</tt>, <tt>ref</tt>, <tt>ref-all</tt>, <tt>first</tt>, <tt>last</tt> and <tt>rest</tt> etc.. Many of the list functions in newLISP also work on strings.</p>
<p>Note that any list or string index in newLISP can be negative starting with -1 from the right side of a list:</p>
<pre>
(set 'L '(a b c d))
(L -1) → d
(L -2) → c
(-3 2 L) → (b c)
(set 'S "abcd")
(S -1) → d
(S -2) → c
(-3 2 S) → "bc")
</pre>
<h3><tt>push</tt> and <tt>pop</tt></h3>
<p>To add to a list use <tt>push</tt>, to eliminate an element from a list use <tt>pop</tt>. Both functions are destructive, changing the contents of a list:</p>
<pre>
(set 'L '(b c d e f))
(push 'a L) → (a b c d e f)
(push 'g L -1) ; push to the end with negative index
(pop L) ; pop first a
(pop L -1) ; pop last g