首页 > 解决方案 > 如何获取 Haskell 中字符串列表的跨度索引?

问题描述

我有一个字符串列表,如下所示:

[Left "hey here are some words",Right "blue",Left "and some other words"]

我想获得每个的跨度索引,因为它们都是一个长字符串的一部分。所以它会输出:

[Left (0, 23), Right (24, 28), Left (29, 49)]

我可以使用mapEither f = fmap $ either (Left . f) (Right . f . fst). 所以我可以使用lengths = mapEither length xs.

此外,我发现我可能需要使用的功能是scanl1,它的工作方式如下:

> scanl1 (+) [23, 4, 20]
[23, 27, 47] 

但我正在努力将它们放在一起。这是我到目前为止所拥有的一切:

import Data.Either

testString = [Left "hey here are some words",Right "blue",Left "and some other words"]

getIndices :: [Either T.Text (T.Text, T.Text)] -> [Integer]
getIndices xs = mapEither length xs where
  mapEither f = fmap $ either (Left . f) (Right . f)
  lengths = mapEither length xs
  spans = scanl1 (mapEither (+)) lengths -- This is wrong

我敢肯定,我让自己变得比需要的更难。

标签: haskell

解决方案


这可能是xy 问题,因为Either通常表示“一个值或另一个值”,并且这些值通常具有不同的含义,并且不会像您的问题那样“聚集在一起”。

由于Functor和的Applicative实例Either不允许您同时在双方工作,您可以构建自己的 fmap 函数:

fmapEither :: (a -> b) -> Either a a -> Either b b
fmapEither f (Left a) = Left $ f a
fmapEither f (Right a) = Right $ f a

然后剩下的就容易一些了:

eitherSpans list = zip <*> tail $ scanl (\acc x -> acc + either length length x) 0 list
--[(0,23),(23,27),(27,47)]

getIndices list = zipWith (\a b -> fmapEither (const a) b) (eitherSpans list) list
--[Left (0,23),Right (23,27),Left (27,47)]
--Or pointfree:
getIndices' = zipWith (fmapEither . const) =<< eitherSpans

编辑

fmapEither您可以使用Data.Bifunctor代替bimapcustom ,但这有点麻烦,因为您必须重复映射函数:

fmapEither f either ≡ bimap f f either

推荐阅读