Haskell 的 Data.Function.on 是做什么的?

qwe*_*iop 7 haskell combinators

我发现很难找到onHaskell 中的示例,而且我不明白 HoogleData.Function页面上的解释。请问,我可以提供其使用示例以及使用它使解决方案更简单或更高效的问题/代码示例吗?

chi*_*chi 13

f假设我们有一个带有两个参数的函数。我们想要一个类似的函数,它的作用是f,但只有在它的两个参数都被其他函数修改之后g

我们可以写一个新的定义

new_f x y = f (g x) (g y)
Run Code Online (Sandbox Code Playgroud)

或者,利用on

new_f = f `on` g
Run Code Online (Sandbox Code Playgroud)

很好的是,后者可以直接使用,无需定义新的符号new_f。(使用 lambda 也是可行的,但没有那么好)

这通常与 一起使用compare,这是一个库二进制函数,用于检查其两个参数之间的顺序。所以,我们可以使用

sortBy (compare `on` name)   somePeopleList
sortBy (compare `on` age)    somePeopleList
sortBy (compare `on` height) somePeopleList
...
Run Code Online (Sandbox Code Playgroud)

根据不同的标准进行排序。上面,我们假设name等是可以从人身上提取相关信息的函数。

(在更现代的 Haskell 中,我们也有sortBy (comparing age) somePeopleList,甚至sortOn age somePeopleList可以完成相同的任务)


Sil*_*olo 9

的类型签名on

on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
Run Code Online (Sandbox Code Playgroud)

而且,实际上,这个函数只有一种合理的(非底层)实现。

f `on` g = \x y -> f (g x) (g y)
Run Code Online (Sandbox Code Playgroud)

或者,像 GHC 源代码中那样用中缀参数编写,

(.+.) `on` g = \x y -> g x .+. g y
Run Code Online (Sandbox Code Playgroud)

所以函数体相当简单。问题是:我们为什么要关心。

on根据我的经验,最常见的用例是使用 来增强排序或比较函数compare

在 Python 中,如果我们想根据名字对人员列表进行排序,我们可能会这样写

my_list.sort(key=lambda person: person.name)
Run Code Online (Sandbox Code Playgroud)

HaskellsortBy采用比较函数进行排序,因此我们可以在 Haskell 中编写相同的内容:

sortBy (\x y -> getName x `compare` getName y) myList
Run Code Online (Sandbox Code Playgroud)

但仅此而已on。所以真的是这样

sortBy (compare `on` getName) myList
Run Code Online (Sandbox Code Playgroud)

“按名称比较排序”。它实际上读起来几乎像英语。

现在,特别是对于排序,这种范例非常常见,以至于更新版本的 Haskell 提供了sortOn,它采用 Python 风格的 key 参数

sortOn getName myList
Run Code Online (Sandbox Code Playgroud)

但是有一整套可以在列表上工作的“By”函数,Haskell 开发人员并没有为每个函数编写“On”变体。

一些例子:

-- Remove entries which have the same name as someone else
nubBy ((==) `on` getName) someList
-- Insert into a list sorted by salary, preserving the ordering
insertBy (compare `on` getSalary) newEmployee employees
-- Find the student with the highest grade
maximumBy (compare `on` getGrade) myStudents
Run Code Online (Sandbox Code Playgroud)

因此,通常on会与名称以“By”结尾的函数一起使用。