Files
modeling-app/docs/kcl/reduce.md
Adam Chalmers 6aa588f09f Bug: KCL formatter removes 'fn' from closures: (#4718)
# Problem

Before this PR, our formatter reformats
```
squares_out = reduce(arr, 0, fn (i, squares)  {
  return 1
})
```
to 
```
squares_out = reduce(arr, 0, (i, squares) {
  return 1
})
```
i.e. it removes the `fn` keyword from the closure. This keyword is required, so, our formatter turned working code into invalid code.

# Cause

When this closure parameter is formatted, the ExprContext is ::Decl, so `Expr::recast` skips adding the `fn` keyword. The reason it's ::Decl is because the `squares_out = ` declaration sets it, and no subsequent call sets the context to something else.

# Solution

When recasting a call expression, set the context for every argument to `ExprContext::Other`.
2024-12-09 19:13:49 -06:00

92 KiB

title, excerpt, layout
title excerpt layout
reduce Take a starting value. Then, for each element of an array, calculate the next value, manual

Take a starting value. Then, for each element of an array, calculate the next value,

using the previous value and the element.

reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue

Arguments

Name Type Description Required
array [KclValue] Yes
start KclValue Any KCL value. Yes
reduce_fn FunctionParam Yes

Returns

KclValue - Any KCL value.

Examples

// This function adds two numbers.
fn add(a, b) {
  return a + b
}

// This function adds an array of numbers.
// It uses the `reduce` function, to call the `add` function on every
// element of the `arr` parameter. The starting value is 0.
fn sum(arr) {
  return reduce(arr, 0, add)
}

/* The above is basically like this pseudo-code:
fn sum(arr):
    let sumSoFar = 0
    for i in arr:
        sumSoFar = add(sumSoFar, i)
    return sumSoFar */


// We use `assertEqual` to check that our `sum` function gives the
// expected result. It's good to check your work!
assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")

Rendered example of reduce 0

// This example works just like the previous example above, but it uses
// an anonymous `add` function as its parameter, instead of declaring a
// named function outside.
arr = [1, 2, 3]
sum = reduce(arr, 0, fn(i, result_so_far) {
  return i + result_so_far
})

// We use `assertEqual` to check that our `sum` function gives the
// expected result. It's good to check your work!
assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")

Rendered example of reduce 1

// Declare a function that sketches a decagon.
fn decagon(radius) {
  // Each side of the decagon is turned this many degrees from the previous angle.
  stepAngle = 1 / 10 * tau()

  // Start the decagon sketch at this point.
  startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])

  // Use a `reduce` to draw the remaining decagon sides.
  // For each number in the array 1..10, run the given function,
  // which takes a partially-sketched decagon and adds one more edge to it.
  fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
    // Draw one edge of the decagon.
    x = cos(stepAngle * i) * radius
    y = sin(stepAngle * i) * radius
    return lineTo([x, y], partialDecagon)
  })

  return fullDecagon
}

/* The `decagon` above is basically like this pseudo-code:
fn decagon(radius):
    let stepAngle = (1/10) * tau()
    let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])

    // Here's the reduce part.
    let partialDecagon = startOfDecagonSketch
    for i in [1..10]:
        let x = cos(stepAngle * i) * radius
        let y = sin(stepAngle * i) * radius
        partialDecagon = lineTo([x, y], partialDecagon)
    fullDecagon = partialDecagon // it's now full
    return fullDecagon */


// Use the `decagon` function declared above, to sketch a decagon with radius 5.
decagon(5.0)
  |> close(%)

Rendered example of reduce 2