Making recursive + if-then-else based macro code simpler ?

196 views
Skip to first unread message

Eric N.

unread,
May 14, 2020, 4:38:31 AM5/14/20
to TiddlyWiki
Hi,

I've written a macro code that gives the expected results, but I find it so complex and hard to read that I wonder if there is a better way.
Any idea ?

What makes the code complex is the expression of nested if-then-else statements with $list widgets.

1/ The data model
  • Tiddlers of type Source <=> with 'so ' prefix
    • can have user defined fields, ex: me_agree
  • Tiddlers of type Note <=> with 'no ' prefix
    • Notes can inherit from a source (tagged by source)
    • Notes can inherit from another note
    • can have the same user defined fields as sources
This can represent a book, a MOOC, a class, ... for example with book : 
  • whole book is represented by a "source"
  • chapters, subchapters and various notes about excerpts are represented by several "notes"
1/ The feature for which I need a macro

Notes inherit the user defined fields in source, but can overwrite the fields, and then the overwritten values are propagated to the sub notes.

In the book example above:
  • book = source: set field me_agree = yes => I globally agree with the book
  • so all the notes, sub notes, etc will display "me_agree = yes"
  • but I strongly disagree with a chapter : setting "me_agree = no" on the note representing the chapter => this chapter note and all sub notes will display "me_agree = no"
  • but I strongly agree with a couple of excerpts in that particular chapter : setting "me_agree = yes" on those notes will display "me_agree = yes"
  • etc.
3/ The code

Macro definition:

\define get_recursive_field(note_title, field_name, calling_note:"")
<!-- calling_note empty only for initial macro call  --->

<$list filter="[<__note_title__>is[tiddler]first[]]" >
  <$list filter="[<__note_title__>get<__field_name__>first[]]" variable=field_value >
    <$list filter="[<__calling_note__>is[tiddler]first[]]">
      <<field_value>> --- //from <$link to=<<__note_title__>> > ancestor </$link>//
    </$list>
    <$list filter="[<__calling_note__>!is[tiddler]first[]]">
      <mark> ''<<field_value>>''  </mark>
    </$list>
  </$list>
  <$list filter="[<__note_title__>!has<__field_name__>first[]]" >
    <$list filter="[<__note_title__>prefix[so ]first[]]" >
      N.A.
    </$list>
    <$list filter="[<__note_title__>!prefix[so ]first[]]" >
      <$set name=new_list filter="[<__note_title__>tags[]prefix[no ]]
        [<__note_title__>tags[]prefix[so ]]" select="0">  
        <$macrocall 
          $name=get_recursive_field 
          note_title=<<new_list>> 
          field_name=<<__field_name__>> 
          calling_note=<<__note_title__>>
        />
      </$set>
    </$list>
  </$list>
</$list>

\end

Macro call :

Agree with //<<currentTiddler>>// ?
<$macrocall $name=get_recursive_field note_title=<<currentTiddler>> field_name=src_url />




Hope I could make myself clear ;-)
Best,
Eric N.

    TonyM

    unread,
    May 14, 2020, 7:38:20 AM5/14/20
    to TiddlyWiki
    Eric

    Love the way you are thinking some of us have done similar things. I think it is as easy as anywhere to do this kind of thing. Of course it has its own complexity. I would look at solving component issues then bring them together in a solution.

    I can help more later but consider a custom new here button which passes more to the child tiddler than a tag. Its easy to creat a tiddler with additional fields. I have posted on setting context before but a simple fieldname={{!!fieldname}} in the creat tiddler can do a lot.

    I will see what other tips I can share when next at my desktop.

    Regards
    Tony

    Eric N.

    unread,
    May 20, 2020, 5:01:17 AM5/20/20
    to TiddlyWiki
    Hi Tony,
    Sorry for the late answer...

    You are right, I am sometimes confusing this google group with a Tiddlywiki FAQ + code review + ideas box + many other things ;-)

    My main question was not really about how to solve my specific problem, but the generic problem of using recursive macros and if then else nested statements in the macros. 

    I'm used to using functions, and in those a simple "return value" will skip the rest of the function code. Clear and short code.

    In macros, I'll display the output "value" but then I have to make sure that the rest of the code is not executed, i.e. that it doesn't display and execute other stuff. That is, I need to make sure that I'll reach the "\end" of the macro without executing anything - as if I did return a value in a function. 

    Because of this "as if", i.e. because I have to make sure I'll reach the end of the macro code without executing anything once I've output what I wanted, I have to use a lot of if then else statements, nested, i.e. a lot of "<$list filter=...>" statements.

    And because of that, the code I write seems to me hard to read, hard to understand later on and hard to maintain. (cf nested lists in my original post)

    And so, this is so surprising to me that I end up with a code I am not comfortable with, not happy with, that I wondered if there was something I completely missed with macros and nested if-then-else statements.


    If that's just the way it works, then OK... Otherwise I'd be happy to learn how to produce a better code :)

    Best,
    And have a good day everyone !


    Eric

    Xavier Cazin

    unread,
    May 20, 2020, 4:54:29 PM5/20/20
    to tiddl...@googlegroups.com
    Hi Eric,

    One TW5 feature that helps a lot with recursions is the emptyMessage attribute of the $list widget. If you put your default values there, you often can manage to put your recursive calls inside a single list. Another feature that helps you keep a compact code while computing conditional values, is the ability to use filters inside {{{}}}. For instance:

    \define get_recursive_field(note_title, field_name:"me_agree")
    <$list filter="[<__note_title__>!is[missing]!subfilter<source-match>!has<__field_name__>]"
           emptyMessage="""<$text text={{{ [<__note_title__>get<__field_name__>] ~[[can't find any value for ]addsuffix<__field_name__>] }}}/>""">
    <$vars heading={{{ [<__note_title__>tags[]subfilter<note-match>] ~[<__note_title__>tags[]subfilter<source-match>] }}}>
    Per <$link to=<<heading>>><<heading>></$link> =>
    <$macrocall $name="get_recursive_field" note_title=<<heading>> />
    </$vars>
    </$list>
    \end

    <$vars source-match="[prefix[so]]" note-match="[prefix[no]]">
      <$macrocall $name="get_recursive_field" note_title=<<currentTiddler>>/>
    </$vars>

    Cheers,
    -- Xavier


    --
    You received this message because you are subscribed to the Google Groups "TiddlyWiki" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to tiddlywiki+...@googlegroups.com.
    To view this discussion on the web visit https://groups.google.com/d/msgid/tiddlywiki/6e270145-48cb-4e8b-88e5-df5226cb360a%40googlegroups.com.

    TonyM

    unread,
    May 20, 2020, 7:47:30 PM5/20/20
    to TiddlyWiki
    Eric

    The first quick tip about recursive processes in tiddlywiki is if a filter is used to call a level it usually describes a list of titles or values which are themselves limited in number, that is they are exhausted eventually and the recursive process returns to the previous call.

    here is an example I shared sometime ago, intentionally made into two macros so the user can find where to insert modifications

    \define each-other-level(filter)
    <li><$link to=<<currentTiddler>> ><$text text=<<currentTiddler>>/></$link></li>
    <ul>
    <$list filter="$filter$">
       
    <<each-other-level $filter$>>
    </$list>
    </
    ul>
    \end
    \define first-level(filter)
    <ul>
    <$list filter="$filter$">
       
    <<each-other-level $filter$>>
    </$list>
    </
    ul>
    \end

    Start in TableOfContents<br>
    <$tiddler tiddler="TableOfContents">

    <<first-level "[is[current]tagging[]]">>

    </$tiddler>

    Although quite complex one insight for the adventerouse into recursive processes in tiddlywiki is the TOC macros which obviously use recursion see $:/core/macros/toc

    Of course if you iterate a hierarchy you need to be careful of entering an infinite loop if one item has a child that is above it in the hierarchy "it may be its own grandfather".

    Bimlases Kin filter is good at turning hierarchies into a flat list of titles (among other things) so this can simplify code. 

    I think using filters can make recursion easy in TiddlyWiki because it naturally limits the iterations, but if you look in my example above, in each-other-level you could add logic to skip calling itself in particular conditions.

    Regards
    Tony 

    Eric N.

    unread,
    May 22, 2020, 6:55:40 AM5/22/20
    to TiddlyWiki
    Hi  Xavier, Tony,

    thanks that looks very helpful I'm going to study that and have some fun !

    Cheers
    Eric
    Reply all
    Reply to author
    Forward
    0 new messages