-5

我正在尝试浏览列表中的字符列表并对当前字符执行某些操作。我的Java相当于我想要完成的是:

public class MyClass {
    void repeat(String s) {
        String newString = "";

        for(int i = 0; i < s.length(); i++) {
          newString += s.charAt(i);
          newString += s.charAt(i);
        }

 public static void main(String args[]) {
    MyClass test = new MyClass();
    test.repeat("abc");
  }
}
4

2 回答 2

3

函数式编程的一大优点是可以将像您这样的模式封装在一个高阶函数中。如果不合适,您仍然可以使用递归。

递归

首先,一个简单的递归解决方案。这背后的想法是它就像一个 for 循环:

recursiveFunction [] = baseCase
recursiveFunction (char1:rest) = (doSomethingWith char1) : (recursiveFunction rest)

因此,让我们以这种形式编写您的repeat函数。基本情况是什么?好吧,如果你repeat是一个空字符串,你会得到一个空字符串。什么是递归?在这种情况下,我们将第一个字符加倍,然后沿着字符串的其余部分递归。所以这是一个递归解决方案:

repeat1 [] = []
repeat1 (c:cs) = c : c : (repeat1 cs)

高阶函数

当您开始编写更多 Haskell 时,您会发现这些递归解决方案通常适合一些重复的模式。幸运的是,标准库包含几个用于这些模式的预定义递归函数:

  • fmap用于使用作为参数给出的函数将列表的每个元素映射到不同的值。例如,fmap (\x -> x + 1)添加1到列表的每个元素。不幸的是,它不能改变列表的长度,所以我们不能单独使用fmap
  • concat用于“展平”嵌套列表。例如,concat [[1,2],[3,4,5]][1,2,3,4,5]
  • foldr/foldl是两个更复杂和通用的函数。有关更多详细信息,请参阅Learn You a Haskell

这些似乎都不能直接满足您的需求。但是,我们可以一起使用concatand fmap

repeat2 list = concat $ fmap (\x -> [x,x]) list

这个想法是fmap改变例如[1,2,3]嵌套列表[[1,1],[2,2],[3,3]],然后 concat 变平。这种从一个元素生成多个元素的模式非常普遍,以至于组合甚至有一个特殊的名称:concatMap. 你像这样使用它:

repeat3 list = concatMap (\x -> [x,x]) list

就个人而言,这就是我repeat在 Haskell 中的写作方式。(嗯,差不多:我会使用eta-reduction来稍微简化它。但在你的水平上,这无关紧要。)这就是为什么在我看来 Haskell 比许多其他语言强大得多:这个 7 行 Java 方法是一行高度可读、惯用的 Haskell!

于 2019-02-27T08:21:01.000 回答
0

正如其他人所建议的那样,从列表理解开始可能是明智的:

-- | Repeat each element of a list twice.
double :: [x] -> [x]
double xs = [d | x <- xs, d <- [x, x]]

但是,无论 的值如何,推导式中的第二个列表总是具有相同数量的元素,这x意味着我们不需要那么多的权力:Applicative接口就足够了。让我们从稍微不同的理解开始:

double xs = xs >>= \x -> [x, x] >>= \d -> pure d

我们可以使用 monad 恒等律立即简化:

double xs = xs >>= \x -> [x, x]

现在我们切换到Applicative,但是让我们为困难的部分留一个洞:

double :: [x] -> [x]
double xs = liftA2 _1 xs [False, True]

编译器让我们知道

_1 :: x -> Bool -> x

由于内部/第二个列表的元素始终相同,并且始终来自当前外部/第一个列表元素,因此我们不必关心Bool

double xs = liftA2 const xs [False, True]

事实上,我们甚至不需要能够区分列表位置:

double xs = liftA2 const xs [(),()]

当然,我们有一个特殊的Applicative方法,(<*),它对应于liftA2 const,所以让我们使用它:

double xs = xs <* [(),()]

然后,如果我们愿意,我们可以xs通过切换到“无点”形式来避免提及:

-- | Repeat each element of a list twice.
double :: [x] -> [x]
double = (<* [(),()])

现在进行测试:

main :: IO ()
main = print $ double [1..3]

这将打印[1,1,2,2,3,3].


double承认可疑价值的轻微概括:

double :: Alternative f => f x -> f x
double = (<* join (<|>) (pure ()))

这将适用于序列和列表:

double (Data.Sequence.fromList [1..3]) = Data.Sequence.fromList [1,1,2,2,3,3]

但对于其他一些情况可能会有点混乱Alternative

double (Just 3) = Just 3
于 2019-02-27T08:09:42.103 回答