List 如何用惯用法编写此函数?
我正在寻找一个函数,该函数在列表的元素上测试谓词,为满足谓词的每个元素创建一个新的列表,并将函数仅应用于该元素 例如:List 如何用惯用法编写此函数?,list,haskell,functional-programming,filtering,higher-order-functions,List,Haskell,Functional Programming,Filtering,Higher Order Functions,我正在寻找一个函数,该函数在列表的元素上测试谓词,为满足谓词的每个元素创建一个新的列表,并将函数仅应用于该元素 例如: someFunction :: (a -> Bool) -> (a -> a) -> [a] -> [[a]] someFunction = ... let ys = someFunction isOdd (* 2) [1..10] {- ys == [[2, 2, 3, 4, 5, ...], [1, 2,
someFunction :: (a -> Bool) -> (a -> a) -> [a] -> [[a]]
someFunction = ...
let ys = someFunction isOdd (* 2) [1..10]
{- ys == [[2, 2, 3, 4, 5, ...],
[1, 2, 6, 4, 5, ...],
[1, 2, 3, 4, 10, ...],
...] -}
在ys
中,除满足谓词并乘以2的第一个元素外,第一个列表与原始列表相等。除第三个元素外,第二个列表也与原始列表相同,依此类推
通过获取满足谓词的值的索引,然后通过索引进行映射,我已经能够编写这样一个函数。但是,这似乎不是很实用,我希望看到一种更惯用的方法。怎么样:
从以下列表开始:
[1,2,3,4]
复制列表n次,n为其大小(:[[]]
):
拆分每个元素上的列表(或多或少是“对角的”)(::[([]),[])]
):
过滤掉标题所在的行。snd
不满足您的谓词
[
([], [1,2,3,4]),
([1,2], [3,4])
]
将您的功能应用于其余头部
[
([], [2,2,3,4])
([1,2], [6,4]),
]
将这些对连在一起
[
[2,2,3,4],
[1,2,6,4]
]
您可以使用手指(如a:D,阅读时将手指移动到每个项目上:D)
r
函数取ps
“已处理元素”和(y:ys)
“待处理元素”
如果您需要线性成本(ps++[y]
operation do it cuadratic),请使用有效的尾部插入结构
使用splitAt
可以编写
someFunction check f xs = map (\(a,(x:b)) -> a ++ [f x] ++ b) $
filter (check.head.snd)
[splitAt n xs | n <- [0..length xs - 1]]
最后(?)@rjanJohansen注释删除init$
(我留下两个版本,我认为这是一个很好的例子)
避免init$
someFunction check f xs =
[ a ++ [f x] ++ b | (a, (x:b)) <- zip (inits xs) (tails xs)
, check x]
(thx@williness)您可以使用标准件或应安装件来组装此功能。公认的答案有正确的拉链线索。我的回答是对相关操作的一般处理,但让我在这里具体说明
我将“具有一个元素孔的列表”的类型定义如下:
data Bwd x = B0 | Bwd x :< x deriving Show
type HoleyList x = (Bwd x, [x])
其思想是,该对的第二个组件位于向后列表和向前列表之间。我可以定义将列表插回一起的函数(在泛型处理中称为upF
)
注意
map snd (selections xs) = xs
map plug (selections xs) = map (const xs) xs
现在我们可以按照巴特克的食谱做了
selectModify :: (a -> Bool) -> (a -> a) -> [a] -> [[a]]
selectModify p f = map (plug . (id *** f)) . filter (p . snd) . selections
也就是说:通过测试过滤选择,将函数应用于聚焦的元素,然后重新插入。如果你有拉链设备,它是一个单衬里,它应该适用于任何可微函子,而不仅仅是列表!工作完成了
> selectModify ((1 ==) . (`mod` 2)) (2*) [1..10]
[[2,2,3,4,5,6,7,8,9,10]
,[1,2,6,4,5,6,7,8,9,10]
,[1,2,3,4,10,6,7,8,9,10]
,[1,2,3,4,5,6,14,8,9,10]
,[1,2,3,4,5,6,7,8,18,10]]
通过浏览这里发布的所有不错的、大多有些别致的解决方案(包括@josejuan最后一个基于zip
的解决方案,这基本上就是我会在匆忙中使用的惯用语),我不禁感到列表缺少了真正直接的、但仍然是惯用的解决方案,即使用explicit,惰性递归——如果someFunction
是一个标准函数,您可能会在标准库中看到这种代码。下面是一个版本(包括您还会看到的go
worker包装):
你说的“对角线”是什么意思?让我把它展开一点。@aochagavia,你看。也许跳过复制步骤,直接压缩初始化和尾部更容易。一个遍历镜头也可以被看作是一个手指。最后一个zip
建议基本上就是我自己快速编写的习惯用法,但我只想补充一点,您不需要init$
部分-最后一对被(a,(x:b))
模式自动过滤掉。@rjanJohansen我想非穷举模式
会报告错误。很好的例子!谢谢!“desugar do表示法”不起作用,因为通常简单的do pe1>=\p->e2
实际上不是-而这种情况下,当p
是一种可能失败的模式时,恰恰是它的断裂处。desugar list理解是一种使用concatMap
的代码,而不是do
。)
someFunction check f xs =
[ a ++ [f x] ++ b | (a, (x:b)) <- zip (inits xs) (tails xs)
, check x]
[e | p <- l, Q] = let ok p = [e | Q]
ok _ = []
in concatMap ok l
data Bwd x = B0 | Bwd x :< x deriving Show
type HoleyList x = (Bwd x, [x])
type InContext x = (HoleyList x, x)
plug :: InContext x -> [x]
plug ((B0, xs), y) = y : xs
plug ((xz :< x, xs), y) = plug ((xz, y : xs), x)
selections :: [x] -> [InContext x]
selections = go B0 where
go xz [] = []
go xz (x : xs) = ((xz, xs), x) : go (xz :< x) xs
map snd (selections xs) = xs
map plug (selections xs) = map (const xs) xs
selectModify :: (a -> Bool) -> (a -> a) -> [a] -> [[a]]
selectModify p f = map (plug . (id *** f)) . filter (p . snd) . selections
> selectModify ((1 ==) . (`mod` 2)) (2*) [1..10]
[[2,2,3,4,5,6,7,8,9,10]
,[1,2,6,4,5,6,7,8,9,10]
,[1,2,3,4,10,6,7,8,9,10]
,[1,2,3,4,5,6,14,8,9,10]
,[1,2,3,4,5,6,7,8,18,10]]
someFunction :: (a -> Bool) -> (a -> a) -> [a] -> [[a]]
someFunction p f xs = go xs where
go [] = []
go (x:xs)
| p x = (f x : xs) : rest
| otherwise = rest
where
rest = map (x :) (go xs)