1
2
3\startcomponent cldmoreonfunctions
4
5\environment cldenvironment
6
7\startchapter[title=More on functions]
8
9\startsection[title=Why we need them]
10
11\index{functions}
12
13In a previous chapter we introduced functions as arguments. At first sight this
14feature looks strange but you need to keep in mind that a call to a \type
15{context} function has no direct consequences. It generates \TEX\ code that is
16executed after the current \LUA\ chunk ends and control is passed back to \TEX.
17Take the following code:
18
19\startbuffer
20context.framed( {
21 frame = "on",
22 offset = "5mm",
23 align = "middle"
24 },
25 context.input("knuth")
26)
27\stopbuffer
28
29\typebuffer
30
31We call the function \type {framed} but before the function body is executed, the
32arguments get evaluated. This means that \type {input} gets processed before
33\type {framed} gets done. As a result there is no second argument to \type
34{framed} and no content gets passed: an error is reported. This is why we need
35the indirect call:
36
37\startbuffer
38context.framed( {
39 frame = "on",
40 align = "middle"
41 },
42 function() context.input("knuth") end
43)
44\stopbuffer
45
46\typebuffer
47
48This way we get what we want:
49
50\startlinecorrection
51\ctxluabuffer
52\stoplinecorrection
53
54The function is delayed till the \type {framed} command is executed. If your
55applications use such calls a lot, you can of course encapsulate this ugliness:
56
57\starttyping
58mycommands = mycommands or { }
59
60function mycommands.framedinput(filename)
61 context.framed( {
62 frame = "on",
63 align = "middle"
64 },
65 function() context.input(filename) end
66end
67
68mycommands.framedinput("knuth")
69\stoptyping
70
71Of course you can nest function calls:
72
73\starttyping
74context.placefigure(
75 "caption",
76 function()
77 context.framed( {
78 frame = "on",
79 align = "middle"
80 },
81 function() context.input("knuth") end
82 )
83 end
84)
85\stoptyping
86
87Or you can use a more indirect method:
88
89\starttyping
90function text()
91 context.framed( {
92 frame = "on",
93 align = "middle"
94 },
95 function() context.input("knuth") end
96 )
97end
98
99context.placefigure(
100 "none",
101 function() text() end
102)
103\stoptyping
104
105You can develop your own style and libraries just like you do with regular \LUA\
106code. Browsing the already written code can give you some ideas.
107
108\stopsection
109
110\startsection[title=How we can avoid them]
111
112\index{delaying}
113\index{nesting}
114
115As many nested functions can obscure the code rather quickly, there is an
116alternative. In the following examples we use \type {test}:
117
118\startbuffer
119\def\test#1{[#1]}
120\stopbuffer
121
122\typebuffer \getbuffer
123
124\startbuffer
125context.test("test 1 ",context("test 2a")," test 3")
126\stopbuffer
127
128\typebuffer
129
130This gives: \ctxluabuffer. As you can see, the second argument is executed before
131the encapsulating call to \type {test}. So, we should have packed it into a
132function but here is an alternative:
133
134\startbuffer
135context.test("test 1 ",context.delayed("test 2a")," test 3")
136\stopbuffer
137
138\typebuffer
139
140Now we get: \ctxluabuffer. We can also delay functions themselves,
141look at this:
142
143\startbuffer
144context.test("test 1 ",context.delayed.test("test 2b")," test 3")
145\stopbuffer
146
147\typebuffer
148
149The result is: \ctxluabuffer. This feature also conveniently permits the use of
150temporary variables, as in:
151
152\starttyping
153local f = context.delayed.test("test 2c")
154context("before ",f," after")
155\stoptyping
156
157Of course you can limit the amount of keystrokes even more by
158creating a shortcut:
159
160\starttyping
161local delayed = context.delayed
162
163context.test("test 1 ",delayed.test("test 2")," test 3")
164context.test("test 4 ",delayed.test("test 5")," test 6")
165\stoptyping
166
167So, if you want you can produce rather readable code and readability of code is
168one of the reasons why \LUA\ was chosen in the first place. This is a good
169example of why coding in \TEX\ makes sense as it looks more intuitive:
170
171\starttyping
172\test{test 1 \test{test 2} test 3}
173\test{test 4 \test{test 5} test 6}
174\stoptyping
175
176The \type {context.nested} variant is now an alias to \type {context.delayed} and
177no longer builds a string representation.
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198\stopsection
199
200\startsection[title=Trial typesetting]
201
202\index {prerolls}
203\index {trial typesetting}
204
205Some typesetting mechanisms demand a preroll. For instance, when determining the
206most optimal way to analyse and therefore typeset a table, it is necessary to
207typeset the content of cells first. Inside \CONTEXT\ there is a state tagged
208\quote {trial typesetting} which signals other mechanisms that for instance
209counters should not be incremented more than once.
210
211Normally you dont need to worry about these issues, but when writing the code
212that implements the \LUA\ interface to \CONTEXT, it definitely had to be taken
213into account as we either or not can free cached (nested) functions.
214
215You can influence this caching to some extend. If you say
216
217\starttyping
218function()
219 context("whatever")
220end
221\stoptyping
222
223the function will be removed from the cache when \CONTEXT\ is not in the trial
224typesetting state. You can prevent removal of a function by returning \type
225{true}, as in:
226
227\starttyping
228function()
229 context("whatever")
230 return true
231end
232\stoptyping
233
234Whenever you run into a situation that you dont get the outcome that you expect,
235you can consider returning \type {true}. However, keep in mind that it will take
236more memory, something that only matters on big runs. You can force flushing the
237whole cache by:
238
239\starttyping
240context.restart()
241\stoptyping
242
243An example of an occasion where you need to keep the function available is in
244repeated content, for instance in headers and footers.
245
246\starttyping
247context.setupheadertexts {
248 function()
249 context.pagenumber()
250 return true
251 end
252}
253\stoptyping
254
255Of course it is not needed when you use the following method:
256
257\starttyping
258context.pagenumber("pagenumber")
259\stoptyping
260
261Because here \CONTEXT\ itself deals with the content driven by the keyword \type
262{pagenumber}.
263
264\stopsection
265
266\startsection[title=Steppers]
267
268The \type {context} commands are accumulated within a \type {\ctxlua} call and
269only after the call is finished, control is back at the \TEX\ end. Sometimes you
270want (in your \LUA\ code) to go on and pretend that you jump out to \TEX\ for a
271moment, but come back to where you left. The stepper mechanism permits this.
272
273A not so practical but nevertheless illustrative example is the following:
274
275\startbuffer
276\startluacode
277 context.stepwise (function()
278 context.startitemize()
279 context.startitem()
280 context.step("BEFORE 1")
281 context.stopitem()
282 context.step("\\setbox0\\hbox{!!!!}")
283 context.startitem()
284 context.step("%p",tex.getbox(0).width)
285 context.stopitem()
286 context.startitem()
287 context.step("BEFORE 2")
288 context.stopitem()
289 context.step("\\setbox2\\hbox{????}")
290 context.startitem()
291 context.step("%p",tex.getbox(2).width)
292 context.startitem()
293 context.step("BEFORE 3")
294 context.stopitem()
295 context.startitem()
296 context.step("\\copy0\\copy2")
297 context.stopitem()
298 context.startitem()
299 context.step("BEFORE 4")
300 context.startitemize()
301 context.stepwise (function()
302 context.step("\\bgroup")
303 context.step("\\setbox0\\hbox{>>>>}")
304 context.startitem()
305 context.step("%p",tex.getbox(0).width)
306 context.stopitem()
307 context.step("\\setbox2\\hbox{<<<<}")
308 context.startitem()
309 context.step("%p",tex.getbox(2).width)
310 context.stopitem()
311 context.startitem()
312 context.step("\\copy0\\copy2")
313 context.stopitem()
314 context.startitem()
315 context.step("\\copy0\\copy2")
316 context.stopitem()
317 context.step("\\egroup")
318 end)
319 context.stopitemize()
320 context.stopitem()
321 context.startitem()
322 context.step("AFTER 1\\par")
323 context.stopitem()
324 context.startitem()
325 context.step("\\copy0\\copy2\\par")
326 context.stopitem()
327 context.startitem()
328 context.step("\\copy0\\copy2\\par")
329 context.stopitem()
330 context.startitem()
331 context.step("AFTER 2\\par")
332 context.stopitem()
333 context.startitem()
334 context.step("\\copy0\\copy2\\par")
335 context.stopitem()
336 context.startitem()
337 context.step("\\copy0\\copy2\\par")
338 context.stopitem()
339 context.stopitemize()
340end)
341\stopluacode
342\stopbuffer
343
344\typebuffer
345
346This gives an (ugly) itemize with a nested one:
347
348\getbuffer
349
350As you can see in the code, the \type {step} call accepts multiple arguments, but
351when more than one argument is given the first one is treated as a formatter.
352
353\stopsection
354
355\stopchapter
356
357\stopcomponent
358 |