diff options
Diffstat (limited to 'doc/context/sources/general/manuals/cld/cld-moreonfunctions.tex')
-rw-r--r-- | doc/context/sources/general/manuals/cld/cld-moreonfunctions.tex | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/cld/cld-moreonfunctions.tex b/doc/context/sources/general/manuals/cld/cld-moreonfunctions.tex new file mode 100644 index 000000000..fab22515e --- /dev/null +++ b/doc/context/sources/general/manuals/cld/cld-moreonfunctions.tex @@ -0,0 +1,354 @@ +% language=uk + +\startcomponent cld-moreonfunctions + +\environment cld-environment + +\startchapter[title=More on functions] + +\startsection[title=Why we need them] + +\index{functions} + +In a previous chapter we introduced functions as arguments. At first sight this +feature looks strange but you need to keep in mind that a call to a \type +{context} function has no direct consequences. It generates \TEX\ code that is +executed after the current \LUA\ chunk ends and control is passed back to \TEX. +Take the following code: + +\startbuffer +context.framed( { + frame = "on", + offset = "5mm", + align = "middle" + }, + context.input("knuth") +) +\stopbuffer + +\typebuffer + +We call the function \type {framed} but before the function body is executed, the +arguments get evaluated. This means that \type {input} gets processed before +\type {framed} gets done. As a result there is no second argument to \type +{framed} and no content gets passed: an error is reported. This is why we need +the indirect call: + +\startbuffer +context.framed( { + frame = "on", + align = "middle" + }, + function() context.input("knuth") end +) +\stopbuffer + +\typebuffer + +This way we get what we want: + +\startlinecorrection +\ctxluabuffer +\stoplinecorrection + +The function is delayed till the \type {framed} command is executed. If your +applications use such calls a lot, you can of course encapsulate this ugliness: + +\starttyping +mycommands = mycommands or { } + +function mycommands.framed_input(filename) + context.framed( { + frame = "on", + align = "middle" + }, + function() context.input(filename) end +end + +mycommands.framed_input("knuth") +\stoptyping + +Of course you can nest function calls: + +\starttyping +context.placefigure( + "caption", + function() + context.framed( { + frame = "on", + align = "middle" + }, + function() context.input("knuth") end + ) + end +) +\stoptyping + +Or you can use a more indirect method: + +\starttyping +function text() + context.framed( { + frame = "on", + align = "middle" + }, + function() context.input("knuth") end + ) +end + +context.placefigure( + "none", + function() text() end +) +\stoptyping + +You can develop your own style and libraries just like you do with regular \LUA\ +code. Browsing the already written code can give you some ideas. + +\stopsection + +\startsection[title=How we can avoid them] + +\index{delaying} +\index{nesting} + +As many nested functions can obscure the code rather quickly, there is an +alternative. In the following examples we use \type {test}: + +\startbuffer +\def\test#1{[#1]} +\stopbuffer + +\typebuffer \getbuffer + +\startbuffer +context.test("test 1 ",context("test 2a")," test 3") +\stopbuffer + +\typebuffer + +This gives: \ctxluabuffer. As you can see, the second argument is executed before +the encapsulating call to \type {test}. So, we should have packed it into a +function but here is an alternative: + +\startbuffer +context.test("test 1 ",context.delayed("test 2a")," test 3") +\stopbuffer + +\typebuffer + +Now we get: \ctxluabuffer. We can also delay functions themselves, +look at this: + +\startbuffer +context.test("test 1 ",context.delayed.test("test 2b")," test 3") +\stopbuffer + +\typebuffer + +The result is: \ctxluabuffer. This feature also conveniently permits the use of +temporary variables, as in: + +\starttyping +local f = context.delayed.test("test 2c") +context("before ",f," after") +\stoptyping + +Of course you can limit the amount of keystrokes even more by +creating a shortcut: + +\starttyping +local delayed = context.delayed + +context.test("test 1 ",delayed.test("test 2")," test 3") +context.test("test 4 ",delayed.test("test 5")," test 6") +\stoptyping + +So, if you want you can produce rather readable code and readability of code is +one of the reasons why \LUA\ was chosen in the first place. This is a good +example of why coding in \TEX\ makes sense as it looks more intuitive: + +\starttyping +\test{test 1 \test{test 2} test 3} +\test{test 4 \test{test 5} test 6} +\stoptyping + +There is also another mechanism available. In the next example the second +argument is actually a string. + +\starttyping +local nested = context.nested + +context.test("test 8",nested.test("test 9"),"test 10") +\stoptyping + +There is a pitfall here: a nested context command needs to be flushed explicitly, +so in the case of: + +\starttyping +context.nested.test("test 9") +\stoptyping + +a string is created but nothing ends up at the \TEX\ end. Flushing is up to you. +Beware: \type {nested} only works with the regular \CONTEXT\ catcode regime. + +\stopsection + +\startsection[title=Trial typesetting] + +\index {prerolls} +\index {trial typesetting} + +Some typesetting mechanisms demand a preroll. For instance, when determining the +most optimal way to analyse and therefore typeset a table, it is necessary to +typeset the content of cells first. Inside \CONTEXT\ there is a state tagged +\quote {trial typesetting} which signals other mechanisms that for instance +counters should not be incremented more than once. + +Normally you don't need to worry about these issues, but when writing the code +that implements the \LUA\ interface to \CONTEXT, it definitely had to be taken +into account as we either or not can free cached (nested) functions. + +You can influence this caching to some extend. If you say + +\starttyping +function() + context("whatever") +end +\stoptyping + +the function will be removed from the cache when \CONTEXT\ is not in the trial +typesetting state. You can prevent removal of a function by returning \type +{true}, as in: + +\starttyping +function() + context("whatever") + return true +end +\stoptyping + +Whenever you run into a situation that you don't get the outcome that you expect, +you can consider returning \type {true}. However, keep in mind that it will take +more memory, something that only matters on big runs. You can force flushing the +whole cache by: + +\starttyping +context.restart() +\stoptyping + +An example of an occasion where you need to keep the function available is in +repeated content, for instance in headers and footers. + +\starttyping +context.setupheadertexts { + function() + context.pagenumber() + return true + end +} +\stoptyping + +Of course it is not needed when you use the following method: + +\starttyping +context.pagenumber("pagenumber") +\stoptyping + +Because here \CONTEXT\ itself deals with the content driven by the keyword \type +{pagenumber}. + +\stopsection + +\startsection[title=Steppers] + +The \type {context} commands are accumulated within a \type {\ctxlua} call and +only after the call is finished, control is back at the \TEX\ end. Sometimes you +want (in your \LUA\ code) to go on and pretend that you jump out to \TEX\ for a +moment, but come back to where you left. The stepper mechanism permits this. + +A not so practical but nevertheless illustrative example is the following: + +\startbuffer +\startluacode + context.stepwise (function() + context.startitemize() + context.startitem() + context.step("BEFORE 1") + context.stopitem() + context.step("\\setbox0\\hbox{!!!!}") + context.startitem() + context.step("%p",tex.getbox(0).width) + context.stopitem() + context.startitem() + context.step("BEFORE 2") + context.stopitem() + context.step("\\setbox2\\hbox{????}") + context.startitem() + context.step("%p",tex.getbox(2).width) + context.startitem() + context.step("BEFORE 3") + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2") + context.stopitem() + context.startitem() + context.step("BEFORE 4") + context.startitemize() + context.stepwise (function() + context.step("\\bgroup") + context.step("\\setbox0\\hbox{>>>>}") + context.startitem() + context.step("%p",tex.getbox(0).width) + context.stopitem() + context.step("\\setbox2\\hbox{<<<<}") + context.startitem() + context.step("%p",tex.getbox(2).width) + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2") + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2") + context.stopitem() + context.step("\\egroup") + end) + context.stopitemize() + context.stopitem() + context.startitem() + context.step("AFTER 1\\par") + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2\\par") + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2\\par") + context.stopitem() + context.startitem() + context.step("AFTER 2\\par") + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2\\par") + context.stopitem() + context.startitem() + context.step("\\copy0\\copy2\\par") + context.stopitem() + context.stopitemize() +end) +\stopluacode +\stopbuffer + +\typebuffer + +This gives an (ugly) itemize with a nested one: + +\getbuffer + +As you can see in the code, the \type {step} call accepts multiple arguments, but +when more than one argument is given the first one is treated as a formatter. + +\stopsection + +\stopchapter + +\stopcomponent |