(不重要的背景信息/动机)
我正在实施一个不同的版本nub,受到Yesod书的劝阻使用它的启发.
map head . group . sort比打电话更有效率nub.但是,在我们的案例中,订单很重要......
所以我开始写一个类似于订单不重要版本的"更好"的小块.我最终得到了这个:
mynub = unsort . map head . groupBy (\x y -> fst x == fst y) . sortBy (comparing fst) . rememberPosition
rememberPosition = flip zip [0..]
unsort = map fst . sortBy (comparing snd)
Run Code Online (Sandbox Code Playgroud)
这肯定会做很多额外的工作,但它应该是O(n log n)而不是原始nub的O(n 2).但这不是重点.问题是,它太长了!它真的不是那么复杂,但它很长(而且我是那些讨厌超过80列的人,或StackOverflow代码块上的水平滚动条).
(问题)
在Haskell中表达长链函数组合的更好方法是什么?
我理解F#中函数组合的基础知识,例如,这里描述的.
也许我错过了一些东西.在>>与<<运营商似乎已经与各功能只需要一个参数的假设定义:
> (>>);;
val it : (('a -> 'b) -> ('b -> 'c) -> 'a -> 'c) = <fun:it@214-13>
> (<<);;
val it : (('a -> 'b) -> ('c -> 'a) -> 'c -> 'b) = <fun:it@215-14>
Run Code Online (Sandbox Code Playgroud)
但是,我想做的事情如下:
let add a b = a + b
let double c = 2*c
let addAndDouble = add >> double // bad!
Run Code Online (Sandbox Code Playgroud)
但即使add输出是输入所需的类型double,也会被拒绝.
我知道我可以用一个元组参数重写add:
let add (a,b) = a + b
Run Code Online (Sandbox Code Playgroud)
或者我可以为第一个函数的每个可能的参数编写一个新的运算符: …
我发现了这个平均函数的实现:
avg :: [Int] -> Int
avg = div . sum <*> length
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?我查看了由于以下原因产生的函数div . sum:
(div . sum) :: (Integral a, Foldable t) => t a -> a -> a
Run Code Online (Sandbox Code Playgroud)
我明白这一点,但我无法说出<*> length它是如何工作的。
我正在为我们创建的API编写演示代码,并且我一直在遇到同样的问题,我正在重复自己,一遍又一遍地恶心.我很痛苦地意识到Java计划添加闭包但我现在无法访问它们.以下是我想在其自己的小角落里插入的地方重复的内容:
public BarObj Foo(Double..._input){
try{
//things that vary per function
//but everything else...
} catch(NullException _null){
m_Logger.error("Null error exception caught in Blah::Foo");
return null;
} catch(Exception ex){
m_Logger.error( ex.getMessage() );
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
关于我认为解决这个问题的唯一方法是将一个Method函数传递给一个函数,该函数带有try-catch逻辑并将其全部包含在另一个函数中,如下所示:
public BarObj MyFunc(Double..._input){
return compose("MyLogic",_input);
}
private BarObj MyLogic(Double..._input)
throws Exception{
//stuff
}
Run Code Online (Sandbox Code Playgroud)
但它看起来很丑陋并带有很多样板.有没有更简单的方法来编写Java中的函数?
我试着用很好的语法实现函数组合,这就是我所拥有的:
from functools import partial
class _compfunc(partial):
def __lshift__(self, y):
f = lambda *args, **kwargs: self.func(y(*args, **kwargs))
return _compfunc(f)
def __rshift__(self, y):
f = lambda *args, **kwargs: y(self.func(*args, **kwargs))
return _compfunc(f)
def composable(f):
return _compfunc(f)
@composable
def f1(x):
return x * 2
@composable
def f2(x):
return x + 3
@composable
def f3(x):
return (-1) * x
print f1(2) #4
print f2(2) #5
print (f1 << f2 << f1)(2) #14
print (f3 >> f2)(2) #1
print (f2 >> f3)(2) #-5
Run Code Online (Sandbox Code Playgroud)
它适用于整数,但在列表/元组上失败: …
这很好用:
Func<string, string> func1 = s => s + "func";
ViewState["function"] = func1;
Run Code Online (Sandbox Code Playgroud)
但是,这不是:
Func<string, string> func1 = s => s + "func";
Func<string, string> func2 = s => func1(s);
ViewState["function"] = func2;
Run Code Online (Sandbox Code Playgroud)
它会抛出运行时序列化异常: Type 'MyProjectName._Default+<>c__DisplayClass3' in Assembly 'MyProjectName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
现在,我可以解决这个问题,但是我想了解为什么会发生这种情况,以便将来除了在序列化之前编写函数之外我别无选择,我将有一个解决方案.
我有一些谓词,例如:
is_divisible_by_13 = lambda i: i % 13 == 0
is_palindrome = lambda x: str(x) == str(x)[::-1]
Run Code Online (Sandbox Code Playgroud)
并希望在逻辑上将它们组合在一起,如:
filter(lambda x: is_divisible_by_13(x) and is_palindrome(x), range(1000,10000))
Run Code Online (Sandbox Code Playgroud)
现在的问题是:这种组合可以用无点样式编写,例如:
filter(is_divisible_by_13 and is_palindrome, range(1000,10000))
Run Code Online (Sandbox Code Playgroud)
这当然不具有期望的效果,因为λ函数的真值是True和and并且or是短路运算符.我想出的最接近的事情是定义一个类P,它是一个简单的谓词容器,它实现__call__()并拥有方法and_()并or_()组合谓词.定义P如下:
import copy
class P(object):
def __init__(self, predicate):
self.pred = predicate
def __call__(self, obj):
return self.pred(obj)
def __copy_pred(self):
return copy.copy(self.pred)
def and_(self, predicate):
pred = self.__copy_pred()
self.pred = lambda x: pred(x) and predicate(x) …Run Code Online (Sandbox Code Playgroud) 为什么haskell需要多个重写规则,具体取决于函数组合技术和长度?有办法避免这种情况吗?
例如,给出以下代码......
{-# RULES
"f/f" forall a. f ( f a ) = 4*a
#-}
f a = 2 * a
Run Code Online (Sandbox Code Playgroud)
这适用于
test1 = f ( f 1 )
Run Code Online (Sandbox Code Playgroud)
但是我们需要添加规则
test2 = f . f $ 1
Run Code Online (Sandbox Code Playgroud)
和
test3 = f $ f 1
Run Code Online (Sandbox Code Playgroud)
给我们留下以下规则
{-# RULES
"f/f1" forall a. f ( f a ) = 4 * a
"f/f2" forall a. f . f $ a = 4 * a
"f/f3" forall a. f $ f $ a = …Run Code Online (Sandbox Code Playgroud) 我来自斯卡拉.所以我经常这样做:
println((1 to 10).filter(_ < 3).map(x => x*x))
Run Code Online (Sandbox Code Playgroud)
在Haskell中,在我发现我可以使用$和删除所有嵌套的括号之后.,我最近发现自己写了:
putStrLn . show . map (**2) . filter (< 3) $ [1..10]
Run Code Online (Sandbox Code Playgroud)
现在,这有效,但代码从右向左阅读,除非我转向阿拉伯语,否则我很难理解.
有没有其他技巧让我从左到右连接功能?或者这只是Haskell惯用的方式?
有没有一种规范的方式来表达一个函数,它是一个有根函数树的组合?
这是一个具体的例子,我的意思是"功能树的组成".取一个根节点的树,其节点由函数标记,如下所示:
节点处的每个函数都是其子节点处的函数的组合.与树相关联的函数本身就是组合
F = a0(b0(c0(e0, e1, e2)), b1(d0(f0), d1(g0, g1)))
Run Code Online (Sandbox Code Playgroud)
更明确地,F是由叶子上的函数计算的6个参数的函数:
F(x0, ... , x5) == a0(b0(c0(e0(x0), e1(x1), e2(x2))),
b1(d0(f0(x3)), d1(g0(x4), g1(x5))))
Run Code Online (Sandbox Code Playgroud)
一般问题
T,以及一个L与节点对应的函数列表T,是否有一种规范的方法来编写F参数的函数T并L返回L根据树结构化的函数的组合T?以这种方式,组合物的"布线" - 树T- 与其内部"组件" - 列表分开L."规范"解决方案尤其应该包括自然适应该问题的表示T和L自然适应.
我怀疑这个问题在函数式编程语言中有一个简单的解决方案,但理想情况下我希望有一个像Python这样的动态类型命令式语言的解决方案,类似于
def treecomp(tree, list_of_funcs):
...
return function
F = treecomp(T, L)
Run Code Online (Sandbox Code Playgroud)
与此同时,我想出了自己的解决方案(发布在下面).
虽然我对其经济和概念简单性感到满意,但我仍然对其他本质上不同的方法感兴趣,特别是那些利用Python中缺乏或支持不足的另一种语言的优势的方法.
使用适当的数据结构 - 基本上不会重现所需的输出! - …
python functional-programming hierarchy function-composition