从 Python 转换为 F# 递归函数?

Converting from Python to F# recursive function?

我试图在不使用不可变变量或 List.map 的情况下将此 Python 代码转换为 F#。我还是个新手。

def findMatches(s,l):
    temp=[]
    for i in l:
        if i[0]==s:
            temp.append(i[1])
    temp.sort()
    return temp

l=findMatches("A",[("A",5),("AA",9),("BB",6),("A",0)])
print(l)

我开始于:

let findMatches s l =
    for e1, e2 in list do
        if e1 = s then ...

如果我正朝着正确的方向前进或下一步要去哪里,则不完全是。

在 Python 中,在进行过程中改变 ("change"、"update") 数据是一种常见的做法 - 就像您对 temp 列表,在每一步都改变它。虽然即使在 Python 中也普遍不被接受,但语言本身使其变得非常简单,因此非常诱人。

在 F# 中改变数据仍然是可能的,但语言让它变得困难。故意。好吧,没有 难 - 这只是一些额外的字符,但至少变异数据不是默认设置。这通常是一种更好的方法,甚至 Python 社区现在也承认这一点。

相反,F# 方式(一般而言,函数式 方式)不是改变数据,而是通过转换旧数据来创建新数据。比如,函数的作用("functions" 我的意思是 "mathematical functions")。

特别是,您的 Python 循环似乎正在做的是 (1) 通过 i[0] 过滤 输入列表,然后 (2) 转换(通常称为"mapping")每个元素通过扔掉i[0]只留下i[1],然后(3)排序。这可以用 F# 写成这样:

let findMatches s l =
    l
    |> List.filter (fun (e1, _) -> e1 == s)  // filtering
    |> List.map (fun (_, e2) -> e2)          // mapping
    |> List.sort

此程序不是通过重复修改列表来 "building up" 列表,而是通过三次转换输入列表来创建结果列表:过滤、映射、排序。另外需要注意的是,转换本身是由更小的部分构建的:整个列表的转换是通过转换单个元素的转换来实现的。

对于像这样的简单情况(排序除外),F# 还提供了特殊语法 - 即所谓的 "list comprehensions"。它是语法糖,在幕后执行类似于上述的转换,但语法(可以说)更具可读性:

let findMatches s l =
    [ for e1, e2 in l do
        if e1 == s then yield e2
    ]
    |> List.sort

请注意,虽然这看起来与您的 Python 程序几乎相同,但含义略有不同。该程序不是 "do this, then do that"(又名 "imperative style"),而是 "the result depends on the input in this way"(又名 "functional style")。

我能想到的从Python示例代码到F#的最直接映射:

// I tend to specify the signatures of methods as I find it helpful
let findMatches (s : 'K) (l : seq<'K*'V>): seq<'V> =
  // ResizeArray is an alias for System.Collections.Generic.List<_>
  let temp = ResizeArray ()
  for e1, e2 in l do
    if e1 = s then
      temp.Add e2
  temp.Sort ()
  // F# don't do implicit upcasts like C# so an explicit upcast from
  //  ResizeArray to seq
  upcast temp

正如 Fyodor 在 F# 中提到的那样,习惯用法(即常用方法)是为了避免可变性并更喜欢内置的高阶函数(如过滤器和映射)来处理列表。