【问题标题】:Swap first and last element haskell交换第一个和最后一个元素haskell
【发布时间】:2013-03-18 10:39:17
【问题描述】:

我试图在 haskell 中交换列表的第一个和最后一个元素。我已经尝试过模式匹配、表达式、函数等。这是我最后一次尝试:

cambio xs = [ cabeza++([x]++cola)|x<-xs, cabeza <- init x, cola <- last x, drop 1 x, drop 0 ([init x])]

我的编译器抛出下一个错误:

Couldn't match expected type `Bool' with actual type `[a0]'
    In the return type of a call of `drop'
    In the expression: drop 1 x
    In a stmt of a list comprehension: drop 1 x

谁能帮帮我?我已经尝试这样做了 2 天

【问题讨论】:

  • 我需要自己实现,“reverse”会反转整个列表,而不仅仅是第一个和最后一个
  • 是的,reverse 所做的不仅仅是交换第一个和最后一个元素,但由于没有指定列表的其余部分必须保持原样,因此它算作一种解决方案。您不能声称 reverse 不会交换列表的第一个和最后一个元素,对吗?
  • swap (x:xs) = concat [[last xs], init xs, [x]] 类似的东西?
  • A list comprehension 具有这种形式:[ output-function | variable <- input-set , predicate ]。您似乎拥有列表函数drop 1 x,其中只允许使用谓词(正如编译器所说,布尔函数)。
  • @DarthFennec 我想知道a : b ++ [c] 是否会比concat 更常见,但我认为您的concat 不需要我们加括号或记住优先表是一个优势。跨度>

标签: list haskell


【解决方案1】:

这里有一些提示:

  1. 列表理解无法解决此问题。
  2. 确定基本(琐碎)案例 - 空列表和一个元素的列表。写出涵盖这些情况的方程式。
  3. 在所有其他情况下,输入列表的长度将 >= 2。您想要的列表是

    [z] ++ xs ++ [a]

其中 z 是最后一个元素,a 是输入列表的第一个元素,xs 是输入的中间部分。

现在告诉我(或你自己),如果输入字符串的长度为 k,xs 会有多长?

  1. 写出包含超过 1 个元素的列表情况的方程式。您可以使用 head、last、length、drop 或 take 等函数。

【讨论】:

  • 我发布我自己的尝试的事实证明我没有要求复制、粘贴的东西。即使有人给了我正确的解决方案,如果解释得当,我也看不出是哪个问题
  • 为什么不能用列表理解来解决这个问题?我坚称它们就像函数,但形式不同
  • @user2180528 列表推导不会更改元素的顺序,而是按原样对列表中的元素进行操作。
【解决方案2】:

我认为列表不是执行此操作的最佳数据结构,但它就是这样:

swap list = last list : (init . tail $ list) ++ [head list]

这将需要遍历列表,并且在长列表中会很慢。这就是链表的本质。


更新了提问者的基本案例:

swap [] = [] 
swap [a] = [a] 
swap list = last list : (init . tail $ list) ++ [head list] 

【讨论】:

  • 这对 [] 和 [1] 失败。如果您必须提供完整的解决方案,请确保它们是正确的。
  • 我修复了这些问题,添加了一些基本案例。这是解决方案:swap [a] = [a] swap [] = [] swap list = last list : (init .tail $ list) ++ [head list]
【解决方案3】:

这是一件相当简单的事情,尤其是使用标准列表函数:

swapfl [] = []
swapfl [x] = [x]
swapfl (x:xs) = (last xs : init xs) ++ [x]

或者没有它们(尽管这不太可读,通常不这样做,也不推荐):

swapfl' [] = []
swapfl' [x] = [x]
swapfl' (x:xs) = let (f, g) = sw x xs in f:g
  where sw k [y] = (y, [k])
        sw k (y:ys) = let (n, m) = sw k ys in (n, y:m)

或许多其他方式之一。

我希望这会有所帮助......我知道我没有做太多解释,但坦率地说,就这个功能而言,很难确切地说出你遇到了什么问题,因为你似乎也完全误解了列出理解。我认为如果我解释这些可能是最有益的?

为什么不能用列表理解来解决这个问题?我坚持认为它们就像函数,但形式不同

不是真的。列表推导式对于轻松定义列表很有用,并且它们与数学中的集合构建符号非常密切相关。这对于这个特定的应用程序没有用处,因为虽然它们非常擅长修改列表的元素,但推导式并不擅长重新排序列表。

在推导式中,您包含三个部分:列表中元素的定义、一个或多个输入列表以及零个或多个谓词:

[ definition | x <- input1, y <- input2, predicate1, predicate2 ]

定义描述了我们正在制作的列表的单个元素,根据输入中的箭头指向的变量(在本例中为 x 和 y)。每个输入在箭头右侧都有一个列表,在左侧有一个变量。我们正在创建的列表中的每个元素都是通过将输入列表中的每个元素组合提取到这些变量中来构建的,并使用这些值评估定义部分。例如:

[ x + y | x <- [1, 3], y <- [2, 4] ]

这会生成:

[1 + 2, 1 + 4, 3 + 2, 3 + 4] == [3, 5, 5, 7]

此外,您还可以包含谓词,类似于过滤器。每个谓词都是根据输入元素定义的布尔表达式,并且只要有新的列表元素,就会对每个谓词进行评估。如果任何谓词结果为假,则这些元素不会放入我们正在制作的列表中。

让我们看看你的代码:

cambio xs = [ cabeza++([x]++cola) | x<-xs, cabeza <- init x, cola <- last x,
                                    drop 1 x, drop 0 ([init x])]

这个理解的输入是x &lt;- xscabeza &lt;- init xcola &lt;- last x。第一个意味着xs 中的每个元素都将用于为新列表定义元素,并且每个元素都将被命名为x。其他两个没有任何意义,因为initlast[a] -&gt; a 类型,但是在箭头的右侧所以必须是列表,而x 必须是列表的元素因为它在箭头的左侧,所以为了编译它,xs 必须输入[[[a]]],我敢肯定这不是你想要的。

您使用的谓词是drop 1 xdrop 0 [init x]。我有点理解你试图对第一个元素做什么,删除列表的第一个元素,但这不起作用,因为x 只是列表的一个元素,而不是列表本身。在第二个中,drop 0 表示“从以下列表的开头删除零个元素”,这绝对不会做任何事情。在任何一种情况下,将类似的东西放在谓词中都行不通,因为谓词需要是一个布尔值,这就是你得到编译器错误的原因。这是一个例子:

pos xs = [ x | x <- xs, x >= 0 ]

这个函数接受一个数字列表,删除所有负数,并返回结果。谓词是x &gt;= 0,它是一个布尔表达式。如果表达式的计算结果为 false,则正在计算的元素会从结果列表中过滤掉。

您使用的元素定义是cabeza ++ [x] ++ cola。这意味着“结果列表中的每个元素本身就是一个列表,由列表cabeza 中的所有元素组成,后跟包含x 的单个元素,然后是列表cola 中的所有元素”,其中似乎与您的目标相反。请记住,管道字符之前的部分定义了单个元素,而不是列表本身。另外,请注意,将方括号括在变量周围会创建一个包含该变量且仅包含该变量的新列表。如果你说y = [x],这意味着y包含单个元素x,并没有说明x是否是一个列表。

我希望这有助于澄清一些事情。

【讨论】:

    猜你喜欢
    • 2021-06-08
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 2016-02-22
    • 2012-06-11
    相关资源
    最近更新 更多