mcpField
B
boxlang-functional-programming

boxlang-functional-programming

github.com/ortus-boxlang开发046 次安装1 周前更新
代码仓库

关于 boxlang-functional-programming

boxlang-functional-programming 是面向 BoxLang 开发者的函数式编程参考技能,讲解函数式技术。内容涵盖闭包与 Lambda 的作用域差异、集合操作、函数组合及 Java Stream 流水线。

主要功能

  1. 01区分闭包与 Lambda
  2. 02使用高阶函数和回调
  3. 03转换与查询集合
  4. 04组合函数并进行部分应用
  5. 05构建 Java Stream 流水线

适用场景

  1. 01筛选或转换数组与结构
  2. 02通过惰性流处理集合
  3. 03用函数组合复用操作

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ortus-boxlang/skills --skill boxlang-functional-programming

安装后在 Claude Code 中输入/boxlang-functional-programming确认已加载。

详细介绍

BoxLang 函数式编程

BoxLang 是面向 AI 的软件生产力平台,用于构建、现代化改造和运行应用程序,让开发者与 AI Agent 协同工作。

概述

BoxLang 将函数视为一等值。函数可以存储在变量中、作为参数传递、由其他函数返回,也可以用于函数式管道。 BoxLang 支持闭包、Lambda 和箭头函数,语法简洁且富有表现力。

闭包与 Lambda —— 关键区别

这一差异关系到代码正确性:

语法名称作用域捕获使用场景
(args) => expr闭包✅ 是 — 捕获外围作用域访问外部变量或调用外部函数/BIF
(args) -> exprLambda❌ 否 — 仅使用自身参数完全确定性;只使用传入的参数
function(...) {}UDF/闭包✅ 是 — 赋值给变量或返回时命名函数或复杂的多行函数体
var multiplier = 10

// ✅ Closure (=>) — captures 'multiplier' from outer scope
var scale = ( n ) => n * multiplier
scale( 5 )   // 50

// ✅ Lambda (->) — does NOT capture, only uses its own arguments
var double = ( n ) -> n * 2
double( 5 )  // 10

// ❌ WRONG — lambda trying to access outer 'multiplier' will fail
var scale = ( n ) -> n * multiplier  // Error! multiplier not in lambda scope

经验法则: 如果函数体引用了外围作用域中的任何变量,或调用外部函数/BIF(例如 now()、writeLog() 等),请使用 =>(闭包)。 如果函数体只处理自身参数,请使用 ->(Lambda)。

闭包

闭包使用 => 或 function 关键字捕获外围变量作用域:

// Closure with fat-arrow syntax (captures outer scope)
var greet = ( required string name ) => "Hello, #name#!"
writeOutput( greet( "BoxLang" ) )  // Hello, BoxLang!

// Closure with function keyword
var greet = function( required string name ) {
    return "Hello, #name#!"
}

// Closure capturing outer variables (must use => or function)
function makeCounter() {
    var count = 0
    return () => {    // => required because it captures 'count'
        count++
        return count
    }
}

var counter = makeCounter()
counter()   // 1
counter()   // 2
counter()   // 3

Lambda

Lambda(->)具有确定性——不会捕获外围作用域。仅当函数体独立自足、只使用自身参数时才使用:

// Lambda: (args) -> expression (no return keyword)
var double   = (n) -> n * 2
var square   = (n) -> n ^ 2
var add      = (a, b) -> a + b

double( 5 )   // 10
add( 3, 4 )   // 7

// Block-body lambda (with return)
var process = (item) -> {
    var result = item.trim()
    return result.uCase()   // only uses item, no outer scope
}

箭头函数(=>)

箭头函数使用 =>,创建能够捕获外围作用域的闭包:

// Expression body (implicit return)
var toUpper = (s) => s.uCase()

// Block body with explicit return
var validate = (email) => {
    return email.contains( "@" ) && email.contains( "." )
}

// Single argument — parentheses optional
var increment = n => n + 1

高阶函数

// Passing a lambda as argument (purely processes its own args)
function applyTwice( required function fn, required any value ) {
    return fn( fn( value ) )
}

applyTwice( (n) -> n * 2, 3 )   // 12

// Returning a closure (captures 'factor' from outer scope — use =>)
function multiplierOf( required numeric factor ) {
    return (n) => n * factor    // => because it captures 'factor'
}

var triple = multiplierOf( 3 )
triple( 7 )   // 21

数组函数式方法

所有集合方法都接受闭包或 Lambda 作为回调。

map — 转换每个元素
var numbers = [1, 2, 3, 4, 5]
var doubled = numbers.map( (n) -> n * 2 )   // lambda: only uses 'n'
// [2, 4, 6, 8, 10]

var users = getUsers()
var names = users.map( (u) -> u.getName() )
filter — 保留匹配的元素
var evens = numbers.filter( (n) -> n % 2 == 0 )   // lambda
// [2, 4]

// Closure when filtering against an outer variable
var threshold = 3
var big = numbers.filter( (n) => n > threshold )   // closure: captures 'threshold'

var activeUsers = users.filter( (u) -> u.isActive() )
reduce — 汇总为单个值
var sum = numbers.reduce( (acc, n) -> acc + n, 0 )   // lambda
// 15

var index = users.reduce( function( acc, user ) {
    acc[ user.getId() ] = user
    return acc
}, {} )
each — 遍历并执行副作用
numbers.each( (n) -> writeOutput( n & " " ) )

users.each( function( user, index ) {
    logService.log( "#index#: #user.getEmail()#" )
})
flatMap — 映射后展平一层
var sentences = ["hello world", "foo bar"]
var words = sentences.flatMap( (s) -> s.listToArray( " " ) )
// ["hello", "world", "foo", "bar"]
groupBy — 按键对元素分组
var byStatus = users.groupBy( (u) -> u.getStatus() )
// { "active": [...], "inactive": [...] }
chunk — 拆分为子数组
var batches = [1,2,3,4,5,6,7].chunk( 3 )
// [[1,2,3],[4,5,6],[7]]
unique — 移除重复项
var deduped = [1, 2, 2, 3, 3, 3].unique()
// [1, 2, 3]
reject — 与 filter 相反
var inactive = users.reject( (u) -> u.isActive() )
zip — 配对两个数组中的元素
var keys = ["a", "b", "c"]
var vals = [1, 2, 3]
var pairs = keys.zip( vals )
// [["a",1],["b",2],["c",3]]
transpose — 交换行和列
var matrix = [[1,2,3],[4,5,6]]
var transposed = matrix.transpose()
// [[1,4],[2,5],[3,6]]
sort — 函数式排序
var sorted = users.sort( (a, b) -> a.getLastName().compare( b.getLastName() ) )
find 和 findAll
var admin = users.find( (u) -> u.getRole() == "admin" )
var admins = users.findAll( (u) -> u.getRole() == "admin" )

结构体函数式方法

var config = { host: "localhost", port: 8080, debug: true }

// map — transform values
var upper = config.map( (key, val) -> val.toString().uCase() )

// filter — keep matching entries
var strings = config.filter( (key, val) -> isSimpleValue( val ) && isString( val ) )

// reduce
var summary = config.reduce( (acc, key, val) -> acc & "#key#=#val# ", "" )

// each
config.each( (key, val) -> writeOutput( "#key#: #val#<br>" ) )

函数组合

// Manual composition
function compose( required function f, required function g ) {
    return (x) -> f( g( x ) )
}

var trim      = (s) -> s.trim()
var toLower   = (s) -> s.lCase()
var normalize = compose( toLower, trim )

normalize( "  Hello World  " )   // "hello world"

// Pipeline using array reduce
var pipeline = [
    (s) -> s.trim(),
    (s) -> s.lCase(),
    (s) -> s.replace( " ", "-" )
]

var slug = pipeline.reduce( (val, fn) -> fn( val ), "  Hello World  " )
// "hello-world"

偏应用

function partial( required function fn, any ...boundArgs ) {
    return function() {
        var allArgs = boundArgs.append( arguments.toArray(), true )
        return fn( argumentCollection=allArgs )
    }
}

var add     = (a, b) -> a + b
var addFive = partial( add, 5 )
addFive( 3 )   // 8

使用函数式代码进行解构(v1.12+)

// Destructure in lambda parameters
var pairs = [["Alice", 30], ["Bob", 25]]
pairs.each( ([name, age]) -> writeOutput( "#name# is #age#" ) )

// Spread in function calls
var args = [1, 2, 3]
var result = sum( ...args )

// Spread in array literals
var merged = [...getDefaults(), ...getOverrides()]

记忆化模式

function memoize( required function fn ) {
    var cache = {}
    return function() {
        var key = jsonSerialize( arguments )
        if ( !cache.keyExists( key ) ) {
            cache[ key ] = fn( argumentCollection=arguments )
        }
        return cache[ key ]
    }
}

var expensiveCalc = memoize( (n) -> {
    // ... expensive computation
    return n * n
})

Java Streams 集成

BoxLang 集合提供 .stream() 方法,可返回延迟求值的 Java Stream, 从而支持采用函数式风格的管道,并延后执行。

创建 Streams
// From array (lazy — nothing computed yet)
var stream = [ 1, 2, 3, 4, 5 ].stream()

// From query
var users = queryExecute( "SELECT * FROM users" )
var userStream = users.stream()

// From struct
var structStream = { name: "Ada", age: 36 }.stream()

// From numeric range
var rangeStream = range( 1, 100 ).stream()

// Infinite stream (always use .limit() before collecting)
import java.util.stream.Stream
var infinite = Stream.iterate( 0, ( n ) => n + 1 )
中间操作(惰性)

这些操作会构建管道,但在调用终端操作之前不会执行:

var result = [ 1, 2, 3, 4, 5, 6 ]
    .stream()
    .filter( ( n ) => n % 2 == 0 )        // keep even numbers
    .map( ( n ) => n * 2 )                 // double each
    .distinct()                            // remove duplicates
    .sorted()                              // sort ascending
    .sorted( ( a, b ) => b - a )           // sort descending
    .limit( 3 )                            // take first 3
    .skip( 1 )                             // skip first 1
    .peek( ( n ) => log.debug( n ) )       // side-effect without modifying
    .flatMap( ( arr ) => arr.stream() )    // flatten nested arrays
    .collect()
终端操作(执行管道)
var numbers = [ 1, 2, 3, 4, 5 ]

// collect() — materialize to array
var evens = numbers.stream().filter( ( n ) => n % 2 == 0 ).collect()
// Result: [ 2, 4 ]

// count()
var count = numbers.stream().filter( ( n ) => n > 3 ).count()  // 2

// reduce() — fold into a single value
var sum = numbers.stream().reduce( 0, ( acc, n ) => acc + n )  // 15

// forEach() — side effects
numbers.stream().forEach( ( n ) => writeOutput( n ) )

// anyMatch / allMatch / noneMatch
var hasEven    = numbers.stream().anyMatch( ( n ) => n % 2 == 0 )   // true
var allPositive = numbers.stream().allMatch( ( n ) => n > 0 )        // true
var noNeg      = numbers.stream().noneMatch( ( n ) => n < 0 )       // true

// findFirst()
var first = numbers.stream().filter( ( n ) => n > 3 ).findFirst()
// Returns an Optional — use .get() or .orElse( default )
var value = first.isPresent() ? first.get() : 0
实用 Stream 管道
// Extract active user names, sorted, uppercased
var activeNames = queryExecute( "SELECT * FROM users" )
    .stream()
    .filter( ( user ) => user.active )
    .map( ( user ) => "#user.firstName# #user.lastName#" )
    .map( ( name ) => name.uCase() )
    .sorted()
    .collect()

// Summary struct via reduce
var summary = orders
    .stream()
    .reduce(
        { total: 0, count: 0 },
        ( acc, order ) => ({ total: acc.total + order.amount, count: acc.count + 1 })
    )
并行 Streams
// parallelStream() — processes elements concurrently
var results = largeDataset
    .parallelStream()
    .filter( ( item ) => item.active )
    .map( ( item ) => expensiveTransform( item ) )
    .collect()
// ⚠️ Only use for CPU-bound, stateless operations on large datasets

参考资料

评论

支持 Markdown