Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/eclipse/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 有没有一种非递归的方式来写这个;“分页”;功能?_Haskell - Fatal编程技术网

Haskell 有没有一种非递归的方式来写这个;“分页”;功能?

Haskell 有没有一种非递归的方式来写这个;“分页”;功能?,haskell,Haskell,以下函数不在标准Haskell库中,因此我编写了它: paginate _ [] = [] paginate n xs = let (h, t) = splitAt n xs in h : paginate n t 然后,我想重写它,不使用显式递归,但仍然很容易理解 我尝试了修复版本,但效果不太理想: paginate = fix go where go _ _ [] = [] go v n xs = let (h, t) = splitAt n xs in h : v n

以下函数不在标准Haskell库中,因此我编写了它:

paginate _ [] = []
paginate n xs = let (h, t) = splitAt n xs in h : paginate n t
然后,我想重写它,不使用显式递归,但仍然很容易理解

我尝试了
修复
版本,但效果不太理想:

paginate = fix go
  where
    go _ _ [] = []
    go v n xs = let (h, t) = splitAt n xs in h : v n t 

有没有使用Prelude函数的更简单的解决方案?

当我看到这类问题时,我喜欢思考您想要的函数的“形状”。在本例中,您将从一个列表开始,并生成一个列表列表。这是从单个值开始并生成列表的特殊情况,该列表对应于展开。因此,如果您不介意导入
Data.List
,您可以使用
unfover
整洁地编写函数

让我们看看如何一步一步地推导出这个公式。首先,如上所述,您只需了解
unfover
,并查看它是否适用。这就是一点经验(比如阅读像这样的答案:)的来源

接下来,我们看一下
展开器的类型:

Prelude Data.List> :t unfoldr
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
我们的想法是从一个“内核”值(
b
)开始,然后重复应用一个步长函数(
b->可能(a,b)
),直到我们点击
Nothing
。我们的起始值是我们想要分割成块的列表,因此我们不需要在那里进行任何处理。这意味着我们的最后一步是实现step函数

因为我们从一个值列表开始,我们可以用
[n]
替换
b
;此外,由于我们希望在最后生成一个列表列表,因此可以将
[a]
替换为
[[n]]]
,从而将
a
替换为
[n]
。这为我们提供了阶跃函数必须实现的最终类型:

[n] -> Maybe ([n], [n])
import Data.List (unfoldr)

paginate n = unfoldr go
  where go [] = Nothing -- base case
        go ls = Just (splitAt n ls)
嘿,这看起来非常类似于
splitAt
的类型

splitAt :: Int -> a -> ([a], [a])
事实上,我们可以将
splitAt
的结果包装在
just
中,并满足我们的类型。但这遗漏了一个非常重要的部分:最后的
Nothing
命令
unbover
停止!因此,我们的helper函数需要一个额外的基本情况,以便
[]
返回正确的结果

把所有这些放在一起,我们就有了最终的功能:

[n] -> Maybe ([n], [n])
import Data.List (unfoldr)

paginate n = unfoldr go
  where go [] = Nothing -- base case
        go ls = Just (splitAt n ls)
我认为最终结果相当不错,但我不确定它是否比递归版本更可读

如果您不介意启用扩展(
LambdaCase
),可以通过避免命名
go
,以稍微整洁的方式重写此扩展:

paginate n = unfoldr $ \case
  [] -> Nothing
  ls -> Just (splitAt n ls)

仅供参考,根据Tikhon Jelvis的回答,我最终得出以下结论:

boolMaybe p f x = if p x then Just (f x) else Nothing
groupWith       = unfoldr . boolMaybe null 
paginate        = groupWith . splitAt

这对我来说有点太模糊了。回到递归版本。

这里是Tikhon-Jelvis代码的一个稍加修改的版本:

import Data.Maybe
import Data.List
import Control.Applicative

paginate n = unfoldr $ \xs -> listToMaybe xs *> Just (splitAt n xs)
或者在无点模式下:

paginate n = unfoldr $ (*>) <$> listToMaybe <*> Just . splitAt n
paginate n=unfover$(*>)列表可能只是。斯普利塔特n

总是有这样的情况:

import Data.List

chunks n = takeWhile (not . null) . unfoldr (Just . splitAt n)
但对我来说,新的冠军是

import Data.Maybe                    -- additional imports
import Control.Applicative

chunks n = unfoldr $ (<$) . splitAt n <*> listToMaybe
               --  \xs -> splitAt n xs <$ listToMaybe xs

Data.List.Split
来自
Split
包的
chunksOf
,如果您希望它已经实现的话。这里有一个可爱的小技巧:如果您启用
MonadComprehensions
,您可以对其他类型使用列表理解语法,包括
Maybe
。有了这个扩展,您可以用
[fx | px]
替换
,如果px,那么就用fx代替
,这是一个简洁的习惯用法。为什么要在右侧使用
,在左侧使用
?如果左侧也有
,则更明显的是
属于应用程序的函数。-很好的技巧,使用
*>
if
进行编码。顺便说一句,严格的数据。这个列表版本只是
takeWhile(不是.null)。展开器(Just.splitAt n)
@Will Ness,我更容易测试减少
f g1 g2。。。gn在我的脑海里,而不是f。G1G2。。。gn
。但这是因为我知道,可能使用了什么应用程序实例。我认为,
展开$()常量。splitAt n listToMaybe
Unfover$()更具可读性。康斯特。splitAt n listtomabe
。我确实想区分一个
fmap
和另一个,当然是YMMV。顺便说一句,你的最后一个也是
(
join f x = f x x                     -- W combinator