多参数嵌套纯函数的函数式组合:可行性与实现方法
多参数嵌套纯函数的函数式组合实现问题
背景
我有一个接收18个参数的纯函数,处理后返回结果,内部调用了多个其他纯函数,嵌套深度可达6层。这种写法测试起来特别麻烦,因为顶层函数除了自身逻辑,还要为内部函数收集参数。
极简概念示例
main_function(a, b, c, d, e) = begin x = pure_function_1(a, b, d) y = pure_function_2(a, c, e, x) z = pure_function_3(b, c, y, x) answer = pure_function_4(x,y,z) return answer end
实际示例
calculate_time_dependant_losses( Ap, u, Ac, e, Ic, Ep, Ecm_t, fck, RH, T, cementClass::Char, ρ_1000, σ_p_start, f_pk, t0, ts, t_start, t_end, ) = begin μ = σ_p_start / f_pk fcm = fck + 8 Fr = σ_p_start * Ap _σ_pb = σ_pb(Fr, Ac, e, Ic) _ϵ_cs_t_start_t_end = ϵ_cs_ti_tj(ts, t_start, t_end, Ac, u, fck, RH, cementClass) _ϕ_t0_t_start_t_end = ϕ_t0_ti_tj(RH, fcm, Ac, u, T, cementClass, t0, t_start, t_end) _Δσ_pr_t_start_t_end = Δσ_pr(σ_p_start, ρ_1000, t_end, μ) - Δσ_pr(σ_p_start, ρ_1000, t_start, μ) denominator = 1 + (1 + 0.8 * _ϕ_t0_t_start_t_end) * (1 + (Ac * e^2) / Ic) * ((Ep * Ap) / (Ecm_t * Ac)) shrinkageLoss = (_ϵ_cs_t_start_t_end * Ep) / denominator relaxationLoss = (0.8 * _Δσ_pr_t_start_t_end) / denominator creepLoss = (Ep * _ϕ_t0_t_start_t_end * _σ_pb) / Ecm_t / denominator return shrinkageLoss + relaxationLoss + creepLoss end
我见过针对单参数函数的函数式组合示例(比如点链式调用、管道运算符等),想知道针对这类多参数嵌套纯函数,用函数式编程进行组合是否可行?如果可行,具体该怎么实现?
解决方案
完全可行,核心思路是参数封装+局部函数拆分+函数式串联,结合Julia的特性可以大幅简化结构、降低测试成本。以下是具体实现方式:
1. 用结构体封装所有参数
把分散的18个参数打包成一个自定义结构体,让所有内部函数可以直接从这个"上下文"中提取所需参数,避免手动传递零散参数:
struct LossParams Ap::Float64 u::Float64 Ac::Float64 e::Float64 Ic::Float64 Ep::Float64 Ecm_t::Float64 fck::Float64 RH::Float64 T::Float64 cementClass::Char ρ_1000::Float64 σ_p_start::Float64 f_pk::Float64 t0::Float64 ts::Float64 t_start::Float64 t_end::Float64 end
2. 拆分中间步骤为独立纯函数
把原函数内的临时变量计算逻辑拆成单独的小函数,每个函数只负责单一计算,且接收LossParams实例作为参数:
# 拆分基础衍生参数计算 calc_μ(params::LossParams) = params.σ_p_start / params.f_pk calc_fcm(params::LossParams) = params.fck + 8 calc_Fr(params::LossParams) = params.σ_p_start * params.Ap # 拆分内部函数调用逻辑 calc_σ_pb(params::LossParams) = σ_pb(calc_Fr(params), params.Ac, params.e, params.Ic) calc_ϵ_cs(params::LossParams) = ϵ_cs_ti_tj(params.ts, params.t_start, params.t_end, params.Ac, params.u, params.fck, params.RH, params.cementClass) calc_ϕ(params::LossParams) = ϕ_t0_ti_tj(params.RH, calc_fcm(params), params.Ac, params.u, params.T, params.cementClass, params.t0, params.t_start, params.t_end) calc_Δσ_pr(params::LossParams) = Δσ_pr(params.σ_p_start, params.ρ_1000, params.t_end, calc_μ(params)) - Δσ_pr(params.σ_p_start, params.ρ_1000, params.t_start, calc_μ(params)) # 拆分最终损失计算逻辑 calc_denominator(params::LossParams, ϕ_val) = 1 + (1 + 0.8 * ϕ_val) * (1 + (params.Ac * params.e^2)/params.Ic) * ((params.Ep * params.Ap)/(params.Ecm_t * params.Ac)) calc_shrinkage_loss(params::LossParams, denom, ϵ_val) = (ϵ_val * params.Ep) / denom calc_relaxation_loss(params::LossParams, denom, Δσ_val) = (0.8 * Δσ_val) / denom calc_creep_loss(params::LossParams, denom, ϕ_val, σ_pb_val) = (params.Ep * ϕ_val * σ_pb_val) / params.Ecm_t / denom
3. 用函数组合串联逻辑
拆分后的小函数可以通过管道运算符|>或直接调用的方式串联,顶层函数只负责组织流程,无需再手动传递零散参数:
# 简洁版顶层函数 calculate_time_dependant_losses(params::LossParams) = begin # 计算所有中间值 σ_pb_val = calc_σ_pb(params) ϵ_cs_val = calc_ϵ_cs(params) ϕ_val = calc_ϕ(params) Δσ_pr_val = calc_Δσ_pr(params) denom = calc_denominator(params, ϕ_val) # 计算最终损失总和 calc_shrinkage_loss(params, denom, ϵ_cs_val) + calc_relaxation_loss(params, denom, Δσ_pr_val) + calc_creep_loss(params, denom, ϕ_val, σ_pb_val) end # 管道串联版(适合线性依赖的步骤) calculate_time_dependant_losses_v2(params::LossParams) = params |> (p -> (p, calc_σ_pb(p), calc_ϵ_cs(p), calc_ϕ(p), calc_Δσ_pr(p))) |> ((p, σ_pb_val, ϵ_cs_val, ϕ_val, Δσ_pr_val) -> (p, σ_pb_val, ϵ_cs_val, Δσ_pr_val, calc_denominator(p, ϕ_val))) |> ((p, σ_pb_val, ϵ_cs_val, Δσ_pr_val, denom) -> calc_shrinkage_loss(p, denom, ϵ_cs_val) + calc_relaxation_loss(p, denom, Δσ_pr_val) + calc_creep_loss(p, denom, ϕ_val, σ_pb_val))
4. 优化测试体验
拆分后的每个小函数都可以单独测试,只需构造包含所需参数的LossParams实例即可,无需传递全部18个参数:
# 测试calc_σ_pb函数 test_params = LossParams(1.0, 0.0, 2.0, 0.5, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 'C', 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0) @assert calc_σ_pb(test_params) == σ_pb(100.0*1.0, 2.0, 0.5, 3.0)
可选技巧:柯里化(Currying)
如果不想用结构体封装,也可以用柯里化把多参数函数转换成单参数函数的链式调用,适合参数较少的场景:
# 柯里化σ_pb函数 curry_σ_pb(Fr) = Ac -> e -> Ic -> σ_pb(Fr, Ac, e, Ic) # 在原函数中使用 calculate_time_dependant_losses(Ap, u, Ac, e, Ic, Ep, Ecm_t, fck, RH, T, cementClass, ρ_1000, σ_p_start, f_pk, t0, ts, t_start, t_end) = begin Fr = σ_p_start * Ap σ_pb_val = curry_σ_pb(Fr)(Ac)(e)(Ic) # 其他步骤同理 end
内容的提问来源于stack exchange,提问作者Joseph S
相关产品推荐
相关产品推荐

