【问题标题】:Find the nth element in a tuple using the function below使用以下函数查找元组中的第 n 个元素
【发布时间】:2021-10-08 16:56:25
【问题描述】:

"使用下面的函数和两个参数:

nth :: (a,a,a,a,a) -> Int -> a

其中 Int 值应返回五元素元组的第 Int 值。” 我试过了:

nth (a,b,c,d,e) x = (a,b,c,d,e) !! x

但是 GHC 给了我一个错误信息:

file.hs:11:21: error:
* Couldn't match expected type `[a1]'
              with actual type `(a, b, c, d, e)'
* In the first argument of `(!!)', namely `(a, b, c, d, e)'
  In the expression: (a, b, c, d, e) !! x
  In an equation for `nth':
      nth (a, b, c, d, e) x = (a, b, c, d, e) !! x
* Relevant bindings include
    e :: e (bound at file.hs:11:14)
    d :: d (bound at file.hs:11:12)
    c :: c (bound at file.hs:11:10)
    b :: b (bound at file.hs:11:8)
    a :: a (bound at file.hs:11:6)
    nth :: (a, b, c, d, e) -> Int -> a1
      (bound at file.hs:11:1)

我该怎么办?我应该如何写这个方程的元组部分? 提前感谢您的回答!

【问题讨论】:

  • 实际上有一个简单的解决方法,您只需更改两个符号:nth (a,b,c,d,e) x = [a,b,c,d,e] !! x。它可能不是一个有效的实现,但我认为 n-tuples 不应该是一个同构容器类型,也不是Int 的有效随机访问。加上它很容易输入:)
  • @Javran 当然它不是同质的,所以列表技巧根本行不通——你不能把不同的类型值放在一个列表中。
  • @WillNess 哪一部分不起作用?这个问题需要nth :: (a,a,a,a,a) -> Int -> a 类型的东西,我没有发现正确性问题。
  • @Javran 你是对的,我没有注意这部分并假设了一个更通用的类型。这也没有任何意义,因为我们必须返回 one 类型。哦! (我责怪睡眠不足;))-----您的答案丢失了。 :)
  • @WillNess 我最初只是想将其作为评论留下,因为我自己认为这只是一个技巧而不是“正确的方式”,但是是的,我认为我可以将其转换为详细说明一下。

标签: haskell tuples


【解决方案1】:

您不能使用(!!) :: [a] -> Int -> a,因为正如签名所说,它适用于列表,而不是元组。您可以使用模式匹配并将其实现为:

nth :: (a, a, a, a, a) -> Int -> a
nth (a, _, _, _, _) 0 = a
nth (_, b, _, _, _) 1 = b
nth (_, _, c, _, _) 2 = c
nth (_, _, _, d, _) 3 = d
nth (_, _, _, _, e) 4 = e

有人提议将 3 元组定义为 (a, (b, c)),因此它是一个递归结构,其中 n 元组被定义为 2 -tuple 以 n-1-tuple 作为第二项。但情况并非如此(目前)。

【讨论】:

    【解决方案2】:

    您可以通过将元组的模式匹配与n 上的匹配分开来避免现有模式匹配解决方案中的一些重复:

    nth :: (a, a, a, a, a) -> Int -> a
    nth (a, b, c, d, e) n = case n of
      0 -> a
      1 -> b
      2 -> c
      3 -> d
      4 -> e
    

    你还应该考虑当 n 不在[0, 4] 范围内时你想要发生什么。如所写,您将在运行时收到一个非详尽的模式错误。返回Maybe a 或采用更受限制的类型作为输入会更诚实,例如

    data QuintupleIndex = Zero | One | Two | Three | Four
    

    【讨论】:

    • 我猜go 是为了避免重复nth (a, b, c, d, e) 很多次?如果是这样,您可以nth (a, b, c, d, e) n = case n of ...。如果你必须有“记忆”行为,那就是 LambdaCase:nth (a, b, c, d, e) = \case ...
    • @DanielWagner 是的,只是为了避免重复元组五次。使用case 是一个很好的建议——我想我是毫无意义的。我真的不喜欢向新手推荐语言扩展。我很好奇您指的是什么记忆行为:LambdaCase 解决方案与 case/of 有什么不同的操作语义?
    • 例如,map (nth (1,2,3,4,5)) 将为每个带有f x y = case y of 的列表元素重新模式匹配一​​次,但对于f x = \case(或f x = \y -> case y of)只匹配一次。
    【解决方案3】:

    (从我自己的 cmets 转换而来)

    实际上有一个其他答案中没有提到的简单解决方法:您只需要更改两个符号:

    nth :: (a, a, a, a, a) -> Int -> a
    nth (a,b,c,d,e) x = [a,b,c,d,e] !! x
    
    -- or alternatively
    nth (a,b,c,d,e) = ([a,b,c,d,e] !!)
    

    仍然需要进行模式匹配来查找第 n 个元素,但它现在在 !! 中隐式处理。

    这是可行的,因为列表是同质的,这意味着它的所有元素都是相同的类型。在nth 的左侧,您有模式(a,b,c,d,e),因此类型推断给出了最一般的约束a :: a, b :: b, ...,如错误消息中所示。但是,一旦在右侧看到[a,b,c,d,e],它就会为类型推断提供一个线索,即所有abcde 都属于同一类型!所以类型推断现在可以确定 (a,b,c,d,e) :: (a,a,a,a,a) 类型检查。

    在 Haskell 中,元组并不是像列表那样用 Int 索引的容器类型(与 Python 和其他一些语言中的元组不同),它更像是一个结构,每个字段都有特定的含义(想想它是 C 中的 struct),如果你确实想要有效地索引它,你需要字段选择器而不是自然数:

    data Five a = Five { _1 :: a,  _2 :: a,  _3 :: a,  _4 :: a,  _5 :: a }
    

    现在您可以使用_x 访问字段,但nth 的类型将是:

    nth :: Five a -> (Five a -> a) -> a
    nth v f = f v
    
    -- or pointfreely:
    nth = flip id
    

    (另一个好处是你有一个完整的函数,而不是一个可能导致模式匹配失败并引发异常的函数)

    请注意,字段选择器是函数而不是自然数 - 如果您正在处理列表索引,询问“下一个元素是什么?”是有意义的,但当涉及到结构时则不那么重要。

    【讨论】:

      【解决方案4】:

      (!!) 函数仅适用于列表。

      对于元组,至少是超过 2 个元素的元组,访问元素的唯一方法是直接模式匹配:

      nth :: (a,a,a,a,a) -> Int -> a
      nth (a, _, _, _, _) 0 = a
      nth (_, a, _, _, _) 1 = a
      nth (_, _, a, _, _) 2 = a
      nth (_, _, _, a, _) 3 = a
      nth (_, _, _, _, a) 4 = a
      

      (注意_s,意思是“我不在乎这个值是什么”。你可以给它们取一个名字——例如匹配每行的(a, b, c, d, e),返回值为@987654325 @ 在第一行,b 在第二行,依此类推。但仅命名您关心的值的视觉“噪音”较少。)

      另外请注意,如果整数参数是 0-4 之外的任何值,此函数将因“非详尽模式”错误而崩溃。但是在 5 个元素的列表上使用 (!!) 有类似的缺陷。您可以轻松地在末尾添加默认情况,尽管在使用完全泛型类型a 时这并不容易,因为没有任何值可以用作函数可能被调用的任何类型的返回值!

      【讨论】:

        【解决方案5】:

        使用守卫的简单解决方案如下:

        nth :: (a,a,a,a,a) -> Int -> a
        nth (a, b, c, d, e) i
          | i == 0    = a
          | i == 1    = b
          | i == 2    = c
          | i == 3    = d
          | otherwise = e
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-01-01
          • 2011-08-29
          • 2016-02-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-21
          相关资源
          最近更新 更多