我有一个列表,我希望从右侧加倍此列表中的所有其他元素。
还有另一个相关的问题可以解决这个问题,但它从左边开始翻倍,而不是右边:Haskell: Double every 2nd element in list
例如,在我的情景中,[1,2,3,4]将成为[2,2,6,4],在那个问题中,[1,2,3,4]将成为[1, 4,3,8]。
我该如何实现?
答案 0 :(得分:13)
我认为最佳答案误解了这个问题。标题清楚地表明OP希望将列表右侧的第二,第四等元素加倍。 ØrjanJohansen的回答是正确的,但速度很慢。这是我更有效的解决方案:
doubleFromRight :: [Integer] -> [Integer]
doubleFromRight xs = fst $ foldr (\x (acc, bool) ->
((if bool then 2 * x else x) : acc,
not bool)) ([], False) xs
它从右侧折叠列表。初始值是一个包含空列表和布尔值的元组。布尔值从false开始,每次都翻转。仅当布尔值为真时,该值才乘以2。
答案 1 :(得分:10)
好的,正如@TomEllis所提到的,其他人似乎都将你的问题解释为左边的奇数元素,而不是右边的偶数,正如你的标题所暗示的那样。
由于您从右侧开始检查位置,因此无法知道在找到列表末尾之前要加倍的内容。所以解决方案不能是懒惰的,将需要在返回任何内容之前将整个列表临时存储(即使只是在执行堆栈上)。
鉴于此,最简单的解决方案可能是在左 - 左解决方案之前和之后应用反向:
doubleFromRight = reverse . doubleFromLeft . reverse
答案 2 :(得分:9)
想一想。
double = zipWith ($) (cycle [(*2),id])
编辑我应该注意,这不是我的解决方案,它是(*2)
和id
翻转的关联帖子的解决方案。这就是我说的原因,因为它是如此微不足道的修复。
答案 3 :(得分:3)
直接实施将是:
doubleOddElements :: [Int] -> [Int]
doubleOddElements [] = []
doubleOddElements [x] = [2 * x]
doubleOddElements (x:y:xs) = (2*x):y:(doubleOddElements xs)
答案 4 :(得分:3)
好吧,所以不像其他答案一样优雅或高效,但我从初学者的角度(我是一个)在可读性和基本功能方面写了这个。
从右开始,每隔一个数字增加一倍。
使用此脚本:doubleEveryOther [1,3,6,9,12,15,18]
生成[1,6,6,18,12,30,18]
,doubleEveryOther [1,3,6,9,12,15]
生成[2,3,12,9,24,15]
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther [] = []
doubleEveryOther (x:[]) = [x]
doubleEveryOther (x:y:zs)
| (length (x:y:zs)) `mod` 2 /= 0 = x : y*2 : doubleEveryOther zs
| otherwise = x*2 : y : doubleEveryOther zs
答案 5 :(得分:1)
我的第一个想法是:
doubleOdd (x:xs) = (2*x):(doubleEven xs)
doubleOdd [] = []
doubleEven (x:xs) = x:(doubleOdd xs)
doubleEven [] = []
DiegoNolan的解决方案更优雅,因为功能和序列长度更容易改变,但我花了一点时间才能理解。
添加从右侧操作的要求使其更复杂一些。 foldr
是从右边做某事的巧妙起点,所以让我试试:
doubleOddFromRight = third . foldr builder (id,double,[])
where third (_,_,x) = x
builder x (fx,fy,xs) = (fy, fx, fx x : xs)
double x = 2 * x
这为每个条目交换了两个函数fx
和fy
。要查找任何条目的值,需要遍历列表的末尾,查找长度是奇数还是偶数。
答案 6 :(得分:1)
尝试稍微概括一下问题:因为我们想要从结尾加倍每个第二个元素,我们无法提前知道它是否是每个奇数或甚至从一开始。所以最简单的方法是构造两者,如果整体大小是偶数或奇数,则计算,然后决定。
让我们定义一个捕获:{/ p>的Applicative
数据结构
如下:
import Control.Applicative
import Data.Monoid
import qualified Data.Traversable as T
data Switching m = Switching !Bool m m
deriving (Eq, Ord, Show)
instance Functor Switching where
fmap f (Switching b x y) = Switching b (f x) (f y)
instance Applicative Switching where
pure x = Switching False x x
(Switching False f g) <*> (Switching b2 x y) = Switching b2 (f x) (g y)
(Switching True f g) <*> (Switching b2 x y) = Switching (not b2) (f y) (g x)
因此,遍历列表将产生两个如下所示的列表:
x1 y2 x3 y4 ...
y1 x2 y3 x4 ...
两个zig-zag-ing副本。现在我们可以计算
double2 :: (Num m) => m -> Switching m
double2 x = Switching True (2 * x) x
double2ndRight :: (Num m, T.Traversable f) => f m -> f m
double2ndRight k = case T.traverse double2 k of
Switching True _ y -> y
Switching False x _ -> x
答案 7 :(得分:1)
我只是在学习 Haskell ,所以请找到以下初学者解决方案。我尝试使用有限的酷函数,例如zipWith
,cycle
或reverse
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther [] = []
doubleEveryOther s@(x:xs)
| (length s) `mod` 2 == 0 = (x * 2) : (doubleEveryOther xs)
| otherwise = x : (doubleEveryOther xs)
需要注意的是,当从右侧加倍每个元素时,你可以将加倍分为两种情况:
我在CS194
作为家庭作业的一部分回答了这个问题答案 8 :(得分:1)
这是我的两个解决方案,请注意我在Haskell中完全是初学者。
首先使用列表函数, head , tail 和 lenght :
doubleSecondFromEnd :: [Integer] -> [Integer]
doubleSecondFromEnd [] = [] -- Do nothing on empty list
doubleSecondFromEnd n
| length n `mod` 2 == 0 = head n * 2 : doubleSecondFromEnd (tail n)
| otherwise = head n : doubleSecondFromEnd (tail n)
第二个,类似但使用不同方法仅使用长度功能:
doubleSecondFromEnd2 :: [Integer] -> [Integer]
doubleSecondFromEnd2 [] = [] -- Do nothing on empty list
doubleSecondFromEnd2 (x:y)
| length y `mod` 2 /= 0 = x * 2 : doubleSecondFromEnd2 y
| otherwise = x : doubleSecondFromEnd2 y
答案 9 :(得分:1)
这是我对此CIS 194家庭作业的答案。它仅使用第1讲+ reverse
中介绍的内容来实现。
doubleEveryOtherLeftToRight :: [Integer] -> [Integer]
doubleEveryOtherLeftToRight [] = []
doubleEveryOtherLeftToRight (x:[]) = [x]
doubleEveryOtherLeftToRight (x:y:zs) = x:y*2:(doubleEveryOtherLeftToRight zs)
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther xs = reverse (doubleEveryOtherLeftToRight (reverse xs))
答案 10 :(得分:0)
有些答案似乎没有涉及列表的奇数/偶数长度。
doubleEveryOtherEvenList = zipWith ($) (cycle [(*2),id])
doubleEveryOther :: [Int] -> [Int]
doubleEveryOther n
| length n `mod` 2 == 0 = doubleEveryOtherEvenList n
| otherwise = (head n) : doubleEveryOtherEvenList (tail n)
答案 11 :(得分:0)
参加haskell的edx课程,这是我的noob解决方案。
doubleSecondR :: [Integer] -> [Integer]
doubleSecondR xs = reverse(zipWith (*) (reverse xs) ys)
where ys = repeat' [1,2]
repeat' :: [a] -> [a]
repeat' xs = xs ++ repeat' xs
答案 12 :(得分:0)
我也从CIS 194课程来到这个问题。
我这样做了两种方式。首先,我认为问题的关键点应该只依赖于列出的3个可能来源中提到的函数或编程方式。 course lecture 1,Real World Haskell ch. 1,2和Learn You a Haskell ch. 2。
好的:
reverse
,基本功能,如max
,min
,odd
,even
head
,tail
,... 不行:
foldr
,foldl
,map
第一个解决方案,只使用带计数器的递归:
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther xs = loopDoubles xs 1
loopDoubles :: [Integer] -> Integer -> [Integer]
loopDoubles [] _ = []
loopDoubles xs n = loopDoubles (init xs) (n + 1) ++ [doubleEven (last xs) n]
doubleEven :: Integer -> Integer -> Integer
doubleEven x n = if even n then x * 2 else x
此方法使用递归,但避免计算递归的每个级别的长度。
违反我的上述规则的第二种方法:
doubleEveryOther' :: [Integer] -> [Integer]
doubleEveryOther' xs = map (\x -> if even (fst x) then (snd x) * 2 else snd x) $ zip (reverse [1..n]) xs
where n = length(xs)
第二个工作方法是建立一组反向索引,然后映射这些索引。这确实计算了长度,但只计算一次。
e.g。 [1,1,1,1] -> [(4,1),(3,1),(2,1),(1,1)]
这两项都要求将所有其他元素从右侧加倍。
> doubleEveryOther [1,2,3,4]
[2,2,6,4]
> doubleEveryOther [1,2,3]
[1,4,3]
> doubleEveryOther' [1,2,3,4]
[2,2,6,4]
> doubleEveryOther' [1,2,3]
[1,4,3]
答案 13 :(得分:0)
为简单起见,这是怎么回事?
doubleEveryOtherRev :: [Integer] -> [Integer]
doubleEveryOtherRev l = doubleRev l []
where
doubleRev [] a = a
doubleRev (x:[]) a = (x:a)
doubleRev (x:y:zs) a = doubleRev zs (2*y:x:a)
如果您遵循该课程的推荐,则必须提供反向的数字列表,因为它会在每次反转时将其他所有元素加倍。我认为这与使用两次反向函数不同,另一种是在其他数字之间加倍,因为你不需要第二次知道列表的完整范围。换句话说,它解决了这个课程的问题,但如果我错了,有人会纠正我。
答案 14 :(得分:0)
我猜想OP在研究Haskell CIS194 Course对作业1作业的答案时提出了这个问题。在该课程的那个阶段,几乎没有将Haskell传授给学生,因此尽管上面的答案是正确的,但它们超出了学习学生的理解范围,因为诸如lambda,函数组成(。)甚至库例程等元素尚未介绍长度和反向。这是与课程教学阶段相匹配的答案:
doubleEveryOtherEven :: [Integer] -> [Integer]
doubleEveryOtherEven [] = []
doubleEveryOtherEven (x:y:xs) = x*2 : y : doubleEveryOtherEven xs
doubleEveryOtherOdd :: [Integer] -> [Integer]
doubleEveryOtherOdd (x:[]) = [x]
doubleEveryOtherOdd (x:y:xs) = x : y*2 : doubleEveryOtherOdd xs
integerListLen :: [Integer] -> Integer
integerListLen [] = 0
integerListLen (x:xs) = 1 + integerListLen xs
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther xs
| integerListLen xs `mod` 2 == 0 = doubleEveryOtherEven xs -- also handles empty list case
| otherwise = doubleEveryOtherOdd xs
该计算需要预先知道列表中的元素是偶数还是奇数,以确定应将每对数字中的哪一个数字加倍。但是,基本的Haskell模式匹配仅允许从左到右匹配列表元素(例如:x:xs),这意味着直到到达末尾,您才能确定元素是奇数还是偶数列表中的所有元素,但是到那时为止为时已晚,因为您需要在遍历列表以到达末尾时对每个左侧的元素对进行计算。
解决方案是将加倍逻辑拆分为两个函数-一个处理偶数长度的列表,另一个处理奇数长度的列表。需要第三个函数来确定要调用给定列表的这两个函数中的哪一个,而这又需要一个可以计算列表长度的附加函数,以便我们可以确定列表中的元素是奇数还是偶数(再次,因为在课程的此阶段尚未引入 length 库功能。
此解决方案还与第1周的课程中的建议相符,该课程指出:“ 通过结合许多简单的功能来构建更复杂的功能是一种很好的Haskell风格。”