【发布时间】:2026-02-07 13:15:01
【问题描述】:
我经常有理由在一个映射中查找一些键,然后在另一个映射中查找结果,依此类推,在任意链中,我正在寻求概括该过程。我还希望能够为此目的预先计算地图列表,以表示 AI 项目中可重用的关联链。
显而易见的解决方案似乎是对我称之为“maybe_map”的列表的 map 函数的简单扩展。
let rec maybe_map operation initial_value list =
match list with
| [] -> Some(initial_value)
| first::rest ->
match operation initial_value first with
| None -> None
| Some(subsequent_value) -> maybe_map operation subsequent_value rest
以下是它的使用示例。
let employee_department = Map.ofList([("John", "Sales" ); ("Bob", "IT" )])
let department_country = Map.ofList([("IT", "USA" ); ("Sales", "France")])
let country_currency = Map.ofList([("USA", "Dollar"); ("France", "Euro" )])
let result = maybe_map Map.tryFind "John" [employee_department; department_country; country_currency]
这可以正常工作并产生结果 Some(“Euro”)。当我需要使用地图的域和范围类型不同的数据时,问题就来了。例如,如果我在上面的示例中添加一个汇率映射,即使涉及的单个操作都不会失败,类型检查器也会抱怨。
let exchange_rate = Map.ofList([("Euro", 1.08); ("Dollar", 1.2)])
let result1 = maybe_map Map.tryFind "John" [employee_department; department_country; country_currency; exchange_rate]
类型检查器抱怨 exchange_rate 应该是 Map
let result2 = Map.tryFind "John" employee_department
let result3 = Map.tryFind "Euro" exchange_rate
如果我在 C# 中执行此操作,在 Dictionary
解决这种似乎经常出现的问题的参考是http://codebetter.com/matthewpodwysocki/2009/01/28/much-ado-about-monads-maybe-edition/,它遵循基于单子的方法。其实这里例子中用到的数据都是取自这篇文章。然而,尽管使用 monad 做如此简单的事情看起来就像是用大锤敲碎了坚果,但当地图的域和范围类型不同时,文章中给出的解决方案也会失效。
有谁知道我怎样才能在这方面取得进展,而不会给概念上非常简单的东西带来额外的复杂性?
结语
在考虑了由于这个问题而收到的非常有用的意见后,我们决定这样做。
class chainable
{
protected Dictionary<IComparable, IComparable> items = new Dictionary<IComparable,IComparable>();
public chainable() {}
public chainable(params pair[] pairs) { foreach (pair p in pairs) items.Add(p.key, p.value); }
public void add(IComparable key, IComparable value) { items.Add(key, value); }
public IComparable lookup(IComparable key) { if (items.ContainsKey(key)) return items[key]; else return null; }
public static List<chainable> chain(params chainable[] chainables) { return new List<chainable>(chainables); }
public static IComparable lookup(IComparable key, List<chainable> chain)
{
IComparable value = key;
foreach (chainable link in chain) { value = link.lookup(value); if (value == null) return null; }
return value;
}
}
这个类的第一行实际上是对所需内容的规范,其余的操作直接从它开始。
chainable employee_department = new chainable(new pair("John", "Sales" ), new pair("Bob", "IT" ));
chainable department_country = new chainable(new pair("IT", "USA" ), new pair("Sales", "France"));
chainable country_currency = new chainable(new pair("USA", "Dollar"), new pair("France", "Euro" ));
chainable exchange_rate = new chainable(new pair("Euro", 1.08 ), new pair("Dollar", 1.2 ));
List<chainable> chain = chainable.chain(employee_department, department_country, country_currency, exchange_rate);
IComparable result = chainable.lookup("John", chain);
诚然,可链接类可以在 F# 中构建,但这只是使用 F# 语法编写 C#,因此我们决定去掉中间人。似乎 F# 并不总是能够从所需操作的直接描述中推断出您真正需要操作的数据类型。再次感谢您的帮助(抱歉,不能使用 Haskell)。
【问题讨论】:
-
您的设计是否必须如此注重字符串和字典?部门不能是引用国家对象的对象吗?
-
@svick 不幸的是,映射中涉及的任意“事物”在系统运行之前是未知的。
-
我没想到你会搬到 Haskell :)