// Copyright (c) 2025 GeWuYou
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections.Concurrent;
namespace GFramework.Core.functional.functions;
///
/// 函数式编程扩展方法集合,提供柯里化、偏函数应用、重复执行、安全执行和缓存等功能
///
public static class FunctionExtensions
{
#region 部分应用 - Partial
///
/// Partial:部分应用函数(固定第一个参数)
///
/// 第一个参数的类型
/// 第二个参数的类型
/// 函数返回结果的类型
/// 要部分应用的二参数函数
/// 要固定的第一个参数值
/// 部分应用后的函数,只接受第二个参数
public static Func Partial(
this Func func,
T1 firstArg)
=> x => func(firstArg, x);
#endregion
#region 重复执行 - Repeat
///
/// Repeat:重复执行函数n次
///
/// 输入值的类型
/// 初始输入值
/// 重复执行的次数
/// 要重复执行的函数
/// 经过多次变换后的最终值
public static TSource Repeat(
this TSource value,
int times,
Func func)
{
var result = value;
// 循环执行指定次数的函数调用
for (int i = 0; i < times; i++)
{
result = func(result);
}
return result;
}
#endregion
#region 安全执行 - Try
///
/// Try:安全执行,捕获异常
///
/// 输入值的类型
/// 函数返回结果的类型
/// 要传递给函数的输入值
/// 要安全执行的函数
/// 包含执行状态、结果和错误信息的元组
public static (bool success, TResult? result, Exception? error) Try(
this TSource value,
Func func)
{
try
{
return (true, func(value), null);
}
catch (Exception ex)
{
return (false, default, ex);
}
}
#endregion
#region 缓存 - Memoize
///
/// Memoize:缓存函数结果(线程安全版本)
///
/// 函数输入参数的类型
/// 函数返回结果的类型
/// 要缓存结果的函数
/// 带有缓存功能的包装函数
///
/// 此版本使用ConcurrentDictionary确保线程安全。
/// GetOrAdd是原子操作,可以安全地在多线程环境中使用。
///
public static Func Memoize(
this Func func)
where TSource : notnull
{
var cache = new ConcurrentDictionary();
return x => cache.GetOrAdd(x, func);
}
#endregion
#region 映射 - Map
///
/// Map:对单个对象应用函数
///
/// 源对象类型
/// 映射后的类型
/// 要映射的源对象
/// 转换函数
/// 映射后的对象
public static TResult Map(
this TSource source,
Func selector)
=> selector(source);
#endregion
#region 柯里化 - Curry/Uncurry
///
/// Curry:将二参数函数转换为柯里化形式
///
/// 第一个参数的类型
/// 第二个参数的类型
/// 函数返回结果的类型
/// 要柯里化的二参数函数
/// 柯里化后的函数,接受一个参数并返回另一个函数
public static Func> Curry(
this Func func)
=> x => y => func(x, y);
///
/// Uncurry:将柯里化函数转换回二参数函数
///
/// 第一个参数的类型
/// 第二个参数的类型
/// 函数返回结果的类型
/// 要取消柯里化的函数
/// 恢复为二参数的函数
public static Func Uncurry(
this Func> func)
=> (x, y) => func(x)(y);
#endregion
}