( expr: Expression, f: (x: Expression) => Expression | null )
| 11 | * If `f` returns `null`, the element is not added to the result |
| 12 | */ |
| 13 | export function holdMap( |
| 14 | expr: Expression, |
| 15 | f: (x: Expression) => Expression | null |
| 16 | ): ReadonlyArray<Expression> { |
| 17 | if (!isFunction(expr)) return []; |
| 18 | |
| 19 | let xs = expr.ops; |
| 20 | |
| 21 | const def = expr.operatorDefinition; |
| 22 | |
| 23 | if (!def || xs.length === 0) return xs; |
| 24 | |
| 25 | // f(a, f(b, c), d) -> f(a, b, c, d) |
| 26 | // Ellipsis fold barrier: a `ContinuationPlaceholder` operand marks a |
| 27 | // notational sum/product; do not lift nested associative operands (it would |
| 28 | // tear a coefficient out of an anchor like the `2n` in `Multiply(2, n)`). |
| 29 | const hasContinuation = xs.some((x) => isContinuationOperand(x)); |
| 30 | if (def?.associative && !hasContinuation) |
| 31 | xs = flatten(xs, expr.operator, false); |
| 32 | |
| 33 | // |
| 34 | // Apply the hold as necessary |
| 35 | // |
| 36 | if (def.lazy) return xs; |
| 37 | |
| 38 | const result: Expression[] = []; |
| 39 | for (const x of xs) { |
| 40 | const h = x.operator; |
| 41 | if (h === 'Hold') result.push(x); |
| 42 | else { |
| 43 | const op = h === 'ReleaseHold' && isFunction(x) ? x.op1 : x; |
| 44 | if (op) { |
| 45 | const y = f(op); |
| 46 | if (y !== null) result.push(y); |
| 47 | } |
| 48 | } |
| 49 | } |
no test coverage detected