Claude whining about this:
Problem
A common shell task is: scan lines, and when a line matches a marker, grab the next line. Example: extracting object names from an EnergyPlus IDF, where the name is the field on the line following each Zone, header.
Minimal input (zones.idf):
Zone,
103_VAV-1.6, ! Name
0, ! Direction of Relative North
Zone,
104_IDU-1, ! Name
0, ! Direction of Relative North
Desired output: [103_VAV-1.6 104_IDU-1]
Current state
All the list iterators (map, filter, each, linearSearch) see one element at a time with no access to neighbors, so the natural higher-order style doesn't apply. The workaround is enumerate to smuggle in the index, then indexing back into the original list with nth, plus manual accumulator management:
mshell
zones.idf str readFile lines idfLines!
[] zoneNames!
@idfLines enumerate each. e!
@e :item? trim 'Zone,' = if
@idfLines @e :index? 1 + nth ',' split :0: trim zoneName!
@zoneNames @zonename append zoneNames!
end
end
@zoneNames
Issues with this: it forces two names (e, and re-fetching from @idfLines) for what is conceptually one value, hand-rolls the accumulator that filter/map normally hide, and has a latent out-of-bounds error if the marker is the last line (index + 1 nth would fail).
Proposal
A sliding-window helper, e.g. windows / pairs (([a] int -- [[a]]), à la Rust slice::windows or Clojure partition 2 1), turning [l1 l2 l3] into [[l1 l2] [l2 l3]]. Then the task composes like everything else:
mshell
zones.idf str readFile lines 2 windows
filter. :0: trim 'Zone,' = end
map. :1: ',' split :0: trim end
The last-line edge case disappears for free (a trailing marker simply produces no window). An eachWindow/mapWindow quotation form would also work, but a plain list→list windows composes with the existing filter/map/match vocabulary and seems the smallest addition.
Claude whining about this:
Problem
A common shell task is: scan lines, and when a line matches a marker, grab the next line. Example: extracting object names from an EnergyPlus IDF, where the name is the field on the line following each Zone, header.
Minimal input (zones.idf):
Zone,
103_VAV-1.6, ! Name
0, ! Direction of Relative North
Zone,
104_IDU-1, ! Name
0, ! Direction of Relative North
Desired output: [103_VAV-1.6 104_IDU-1]
Current state
All the list iterators (map, filter, each, linearSearch) see one element at a time with no access to neighbors, so the natural higher-order style doesn't apply. The workaround is enumerate to smuggle in the index, then indexing back into the original list with nth, plus manual accumulator management:
mshell
zones.idfstr readFile lines idfLines![] zoneNames!
@idfLines enumerate each. e!
@e :item? trim 'Zone,' = if
@idfLines @e :index? 1 + nth ',' split :0: trim zoneName!
@zoneNames @zonename append zoneNames!
end
end
@zoneNames
Issues with this: it forces two names (e, and re-fetching from @idfLines) for what is conceptually one value, hand-rolls the accumulator that filter/map normally hide, and has a latent out-of-bounds error if the marker is the last line (index + 1 nth would fail).
Proposal
A sliding-window helper, e.g. windows / pairs (([a] int -- [[a]]), à la Rust slice::windows or Clojure partition 2 1), turning [l1 l2 l3] into [[l1 l2] [l2 l3]]. Then the task composes like everything else:
mshell
zones.idfstr readFile lines 2 windowsfilter. :0: trim 'Zone,' = end
map. :1: ',' split :0: trim end
The last-line edge case disappears for free (a trailing marker simply produces no window). An eachWindow/mapWindow quotation form would also work, but a plain list→list windows composes with the existing filter/map/match vocabulary and seems the smallest addition.