将List中的相邻元素放入Tuples中
Put adjacent elements in List into Tuples
给定一个元素列表:
xs = [a, b, c, d, ... z]
其中 a, b, c
等是任意值的占位符。
我想实现一个生成
的函数 adjacents :: [a] -> [(a, a)]
adjacentValues = [(a, b), (b, c), (c, d), ... (y, z)]
在Haskell中,递归定义相当简洁:
adjacents :: [a] -> [(a, a)]
adjacents (x:xs) = (x, head xs) : adjacents xs
adjacents [] = []
Purescript 有点冗长:
adjacents :: forall a. List a -> List (Tuple a a)
adjacents list = case uncons list of
Nothing -> []
Just {head: x, tail: xs} -> case head xs of
Just next -> Tuple x next : adjacents xs
Nothing -> []
有没有一种不用显式递归(使用折叠)来表达 adjacents
的方法?
免责声明:这个问题同时具有 Purescript 和 Haskell 标签,因为我想将它开放给更广泛的受众。我认为答案不依赖于 haskells 惰性求值语义,因此在两种语言中都有效。
在 Haskell 中,无需显式递归,您可以使用尾部压缩列表。
let a = [1,2,3,4,5,6,7,8,9,0]
a `zip` tail a
=> [(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,0)]
为完整起见,纯脚本解决方案:
adjacent :: forall n. List n -> List (Tuple n n)
adjacent list = zip list $ fromMaybe empty $ tail list
可以更优雅地表达为:
adjacent :: forall n. List n -> List (Tuple n n)
adjacent list = zip list $ drop 1 list
为了说明(基于 zip
的解决方案肯定更好),这里是您的显式递归 Haskell 解决方案,写成展开。没有特别的原因,我把它打成了 one-liner。
{-# LANGUAGE LambdaCase #-}
import Data.List (unfoldr)
adjacent :: [a] -> [(a, a)]
adjacent = unfoldr (\case { x:y:ys -> Just ((x, y), ys); _ -> Nothing })
(请注意,此处的模式匹配处理具有奇数个元素的列表而不会崩溃。)
既然我们已经看过 zip
和 unfoldr
,我们应该使用 foldr
:
adjacent :: [a] -> [(a,a)]
adjacent xs = foldr go (const []) xs Nothing
where
go a r Nothing = r (Just a)
go a r (Just prev) = (prev, a) : r (Just a)
现在,因为每个玩具问题都需要一个 over-engineered 解决方案,下面是您可以用来获得 double-sided 列表融合的方法:
import GHC.Exts (build)
adjacent :: [a] -> [(a,a)]
adjacent xs = build $ \c nil ->
let
go a r Nothing = r (Just a)
go a r (Just prev) = (prev, a) `c` r (Just a)
in foldr go (const nil) xs Nothing
{-# INLINE adjacent #-}
折叠状态,其中状态是最后配对的项目:
在Haskell中:
import Data.List (mapAccumL)
adjacents :: [a] -> [(a, a)]
adjacents [] = []
adjacents (x:xs) = snd $ mapAccumL op x xs
where
op x y = (y, (x,y))
给定一个元素列表:
xs = [a, b, c, d, ... z]
其中 a, b, c
等是任意值的占位符。
我想实现一个生成
adjacents :: [a] -> [(a, a)]
adjacentValues = [(a, b), (b, c), (c, d), ... (y, z)]
在Haskell中,递归定义相当简洁:
adjacents :: [a] -> [(a, a)]
adjacents (x:xs) = (x, head xs) : adjacents xs
adjacents [] = []
Purescript 有点冗长:
adjacents :: forall a. List a -> List (Tuple a a)
adjacents list = case uncons list of
Nothing -> []
Just {head: x, tail: xs} -> case head xs of
Just next -> Tuple x next : adjacents xs
Nothing -> []
有没有一种不用显式递归(使用折叠)来表达 adjacents
的方法?
免责声明:这个问题同时具有 Purescript 和 Haskell 标签,因为我想将它开放给更广泛的受众。我认为答案不依赖于 haskells 惰性求值语义,因此在两种语言中都有效。
在 Haskell 中,无需显式递归,您可以使用尾部压缩列表。
let a = [1,2,3,4,5,6,7,8,9,0]
a `zip` tail a
=> [(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,0)]
为完整起见,纯脚本解决方案:
adjacent :: forall n. List n -> List (Tuple n n)
adjacent list = zip list $ fromMaybe empty $ tail list
可以更优雅地表达为:
adjacent :: forall n. List n -> List (Tuple n n)
adjacent list = zip list $ drop 1 list
为了说明(基于 zip
的解决方案肯定更好),这里是您的显式递归 Haskell 解决方案,写成展开。没有特别的原因,我把它打成了 one-liner。
{-# LANGUAGE LambdaCase #-}
import Data.List (unfoldr)
adjacent :: [a] -> [(a, a)]
adjacent = unfoldr (\case { x:y:ys -> Just ((x, y), ys); _ -> Nothing })
(请注意,此处的模式匹配处理具有奇数个元素的列表而不会崩溃。)
既然我们已经看过 zip
和 unfoldr
,我们应该使用 foldr
:
adjacent :: [a] -> [(a,a)]
adjacent xs = foldr go (const []) xs Nothing
where
go a r Nothing = r (Just a)
go a r (Just prev) = (prev, a) : r (Just a)
现在,因为每个玩具问题都需要一个 over-engineered 解决方案,下面是您可以用来获得 double-sided 列表融合的方法:
import GHC.Exts (build)
adjacent :: [a] -> [(a,a)]
adjacent xs = build $ \c nil ->
let
go a r Nothing = r (Just a)
go a r (Just prev) = (prev, a) `c` r (Just a)
in foldr go (const nil) xs Nothing
{-# INLINE adjacent #-}
折叠状态,其中状态是最后配对的项目:
在Haskell中:
import Data.List (mapAccumL)
adjacents :: [a] -> [(a, a)]
adjacents [] = []
adjacents (x:xs) = snd $ mapAccumL op x xs
where
op x y = (y, (x,y))