从Haskell中的元组中提取第n个元素(其中n和元组被赋予参数)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Haskell中的元组中提取第n个元素(其中n和元组被赋予参数)相关的知识,希望对你有一定的参考价值。

有没有办法在Haskell中编写以下函数:

get_nth_element :: Int->(???)->(???)
get_nth_element n tpl = ...

我对元组而不是列表的解决方案很感兴趣,解决方案很简单。当然,元组可以聚合不同类型的原因。

中间目标是能够编写一个给定元组和值的函数,它返回一个元组,将给定值添加给给定元组。最终目标是编写一个通用的笛卡尔积函数,给定n个(可能不同类型的)列表的元组,返回所有得到的n维元组的笛卡尔积列表(使用,例如,应用运算符<$>, <*>)。

这可以用C ++实现(使用可变参数模板和SFINAE),所以我假设应该有一种方法可以在Haskell中实现。

[更新] C ++(11及以上版本)有std::get<i>(tuple)std::tuple_cat。例如,这可以按预期编译和运行:

#include <iostream>
#include <tuple>

template<typename T, typename ...Args>
std::tuple<T,Args...> grow_tuple(T v, std::tuple<Args...> t)
{
  return std::tuple_cat(std::make_tuple(v), t);
}

int main(void)
{
  std::tuple<int, std::string> t1{1, "Howdy!"};

  int i = 13;
  auto t2 = grow_tuple(i, t1);

  std::cout<<"("<<std::get<0>(t2)
       <<","<<std::get<1>(t2)
       <<","<<std::get<2>(t2)<<")\n";

  std::cout<<"Done!"<<std::endl;
}

本质上,这里需要一种机制来处理可变参数类型列表。我几乎无法相信Haskell没有那个。

[更新]注意:这不是关于从先验已知长度的元组访问先验已知元素。它是关于访问由变量元组中的变量索引给出的元素(长度和类型未知的先验)。前者意味着lambda存取器((_,_,x,_,_) -> x)的固定定义,其中元组和元组长度中的感兴趣位置都是先验已知的。虽然我的问题没有做出任何先验已知的假设。我正在寻找的函数签名不采用这样的lambda访问器,而是采用元组内部位置索引的整数参数和通用元组参数(未知长度和包含类型的通用含义)。因此,这个问题与Haskell - Accessing a Specific Element in a Tuple不同。

答案

经过验证的HList似乎与您的用例相符。

{-# LANGUAGE DataKinds, ExplicitForAll, GADTs, PolyKinds,
             TypeFamilies, TypeOperators, UndecidableInstances
#-}
import GHC.TypeLits(TypeError(..), ErrorMessage(..))

-- HList takes a list of types and creates a type that contains one value of each
data HList (ts :: [*]) where
  (:^:) :: t -> !(HList ts) -> HList (t:ts)
  -- strictness removes extra bottoms compared to tuples
  HNil  :: HList '[]
infixr 5 :^:

-- natural numbers are either zero or the successor of another
data Nat = Zero | Succ Nat
-- singletons
data SNat (n :: Nat) where
  SZero :: SNat Zero
  SSucc :: SNat n -> SNat (Succ n)

-- index into a normal list
type family IndexL (i :: Nat) (xs :: [k]) :: k where
  IndexL Zero (x:xs) = x
  IndexL (Succ n) (x:xs) = IndexL n xs
  IndexL i xs = TypeError (Text "Cannot index "
                      :<>: ShowType i
                      :<>: Text " into the list "
                      :<>: ShowType xs
                          )

-- index into an HList
index :: forall (i :: Nat) xs. SNat i -> HList xs -> IndexL i xs
index SZero (x :^: _) = x
index (SSucc n) (_ :^: xs) = index n xs
-- despite appearances, this function is total
-- you'll get a compiler error if you try to add an HNil case.

有一堆实例(例如ReadEq)要编写/派生以使其有些可用。

main = do let test = 1 :^: "abc" :^: (7 :^: Zero :^: HNil) :^: HNil
          print $ index SZero test + 2
          print $ ord <$> index (SSucc$SZero) test
          print $ index SZero $ index (SSucc$SSucc$SZero) test

插入元素涉及在类型级别上添加type family Inject (x :: k) (i :: Nat) (xs :: [k]) :: [k] where ...以及在值上添加相应的函数。编写Applicative f => HList [f a, f b, ...] -> f (HList [a, b, ...])类型的函数更难(在更多的打字意义上,而不是概念上),但可行。

你没有得到很好的数字文字,但那真的只是“meh”。你可以用TH来纠正它。

另一答案

通常不可能实现,因为元组中的每个元素都有不同的类型

以3个项目为例。在编译时不可能确定输出类型

 nth :: (a,b,c) => Int -> (a,b,c) -> ???

以上是关于从Haskell中的元组中提取第n个元素(其中n和元组被赋予参数)的主要内容,如果未能解决你的问题,请参考以下文章

识别 Haskell 元组中的重复项

在第一个元素在特定范围内的元组中查找元组列表中的最小值

Python中的元组(Tuple)

元组中的元素到 Bag Pig 中的元组

限制 2 元组中元素的出现次数

python学习之第五篇:Python中的元组及其所具有的方法