简体   繁体   English

Haskell,部分反转列表

[英]Haskell, reversing partially a list

I am trying to create a function that takes a list and, if the length of the list is at least 4, it reverses the first and second element and the last two elements of said list. 我正在尝试创建一个接受列表的函数,如果列表的长度至少为4,它将反转所述列表的第一个和第二个元素以及最后两个元素。 Imagine the input is [1,2,3,4,5,6] this should give me [2,1,3,4,6,5]. 想象一下输入是[1,2,3,4,5,6]这应该给我[2,1,3,4,6,5]。

I am trying to figure it out but it's being a pain in the ass. 我试图弄清楚,但是这是一个痛苦。

So, this is what I've got: 所以,这就是我所拥有的:

transf :: [a] -> [a]
transf [] = []
transf xs | length xs >= 4 = ???
          | otherwise = xs

So I thought that I could do a aux function that would only reverse those elements and then calling it on the transf function. 因此,我认为我可以做一个aux函数,该函数只会反转那些元素,然后在transf函数上调用它。 Any help? 有什么帮助吗?

Usually it is better to use pattern matching over using length . 通常,最好在使用length使用模式匹配。 If for instance a list has an infinite length, length will loop forever. 例如,如果列表的长度是无限的,则length将永远循环。 So wen can define a function: 因此,wen可以定义一个函数:

transf :: [a] -> [a]
transf (a:b:c:d:es) = ...
transf x = x

So here we define a pattern (a:b:c:d:es) which is the pattern to define a list with at least four elements ( a , b , c , and d ) and the rest of the list es . 因此,在这里我们定义了一个模式(a:b:c:d:es) ,该模式用于定义一个至少包含四个元素( abcd )以及其余列表es的列表。

Now in that case we know that we have to reverse the first two elements of the list, so the result is something like b : a : ... . 现在,在这种情况下,我们知道必须反转列表的前两个元素,因此结果类似于b : a : ... Now the question is how we calculate the rest of the list. 现在的问题是我们如何计算列表的其余部分。 We can not add c and d to the list, since it is possible that es is the empty list, in which case, we want to reverse d and c . 我们无法将cd添加到列表中,因为es可能是空列表,在这种情况下,我们要反转dc This can result in a lot of cases. 这可能会导致很多情况。

We can however use an as-pattern here, to match both the first two items, as well as the rest of the list (and the rest of the list should contain at least two items). 但是,我们可以在此处使用as模式,以匹配前两个项以及列表的其余部分(并且列表的其余部分应至少包含两个项目)。 Like: 喜欢:

transf :: [a] -> [a]
transf (a:b:cs@(_:_:_)) = b : a : ...
transf x = x

So now we only have to fill in the ... . 所以现在我们只需要填写... This needs to reverse the last two items. 这需要逆转最后两项。 Instead of letting transf have all the fun, we can define a new function swaplast :: [a] -> [a] which swaps the last items. 与其让transf拥有所有的乐趣,我们可以定义一个新的功能swaplast :: [a] -> [a]其交换的最后一个项目。 What to do if the list has no two elements can be decided by ourselves since we know that the list with which we will call the function will have at least two elements. 如果列表没有两个元素,该怎么办可以由我们自己决定,因为我们知道调用该函数的列表将至少包含两个元素。 We can for instance decide that empty lists and singleton lists, are simply returned without a modification. 例如,我们可以决定简单地返回空列表和单例列表,而无需进行修改。 So we can implement such function as: 因此我们可以实现以下功能:

swaplast :: [a] -> [a]
swaplast [] = []
swaplast [a, b] = [b, a]
swaplast (a:bs) = a : swaplast bs

So then we obtain the implementation: 因此,我们获得了实现:

transf :: [a] -> [a]
transf (a:b:cs@(_:_:_)) = b : a : swaplast cs
transf x = x

swaplast :: [a] -> [a]
swaplast [] = []
swaplast [a, b] = [b, a]
swaplast (a:bs) = a : swaplast bs

We write four functions. 我们编写四个函数。 That makes reasoning a lot simpler and the functions easier to maintain: 这使得推理变得更加简单,并且功能更易于维护:

reverseFirst :: [a] -> [a]
reverseFirst (x:y:xs) = y : x : xs
reverseFirst xs       = xs

reverseLast :: [a] -> [a]
reverseLast [x, y] = [y, x]
reverseLast (x:xs) = x : reverseLast xs
reverseLast xs     = xs

lengthAtLeast :: [a] -> Int -> Bool
lengthAtLeast xs n = not $ null $ drop (n - 1) xs

Now, with those helpers, it's easy to write the fourth and last function: 现在,有了这些助手,编写第四个也是最后一个函数很容易:

transf :: [a] -> [a]
transf xs
  | xs `lengthAtLeast` 4 = reverseLast $ reverseFirst xs
  | otherwise           = xs

We can drop lengthAtLeast if we pattern match: 如果我们进行模式匹配,我们可以删除lengthAtLeast

transf :: [a] -> [a]
transf (a:b:xs@(_:_:_)) = b : a : reverseLast xs
transf xs               = xs

Exercise 行使

Make the amount of characters reversed at the end and start customizeable: 最后使字符数量反转并开始自定义:

transform :: Int -> Int -> [a] -> [a]
transform = ...

It should work like this: 它应该像这样工作:

transform 1 1 [1,2,3,4] = [1,2,3,4]
transform 1 2 [1,2,3,4] = [1,2,4,3]
transform 1 3 [1,2,3,4] = [1,4,3,2]
transform 1 4 [1,2,3,4] = [1,2,3,4] -- list isn't 5 elements long
transform 2 2 [1,2,3,4] = [2,1,4,3]

Something like this, works only for finite lists 这样的事情只适用于有限列表

-- works only for finite lists
f :: [a] -> [a]
f ls
    | n <= 4     = ls
    | otherwise  = let (a:b:hs, [x, y]) = splitAt (n - 2) ls 
                   in  b:a:(hs ++ [y, x])
    where
        n = length ls

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM