Подтвердить что ты не робот

Как преобразовать список в кортеж в Haskell?

Как лучше всего преобразовать список в кортеж в Haskell:

[1,2,3,4,5,6] -> (1,2,3,4,5,6)
4b9b3361

Ответ 1

В общем, вы не можете. Каждый размер кортежа является отдельным типом, тогда как списки любой длины - это один тип. Таким образом, нет хорошего способа написать функцию, которая берет список и возвращает кортеж той же длины - у него не будет корректного типа возврата.

Например, у вас могут быть такие функции, как:

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

tuplify3 :: [a] -> (a,a,a)
tuplify3 [x,y,z] = (x,y,z)

... но не тот, который выполняет работу обоих.

Вы можете написать общую версию, используя различные виды метапрограмм, но вы редко хотели бы.

Обратите внимание, что эта же проблема относится и к другим вещам, таким как запись экземпляров классов для разных кортежей. Посмотрите исходный код для Data.Tuple из стандартных библиотек!

Ответ 2

Шаблон Haskell находится так близко, как вы можете получить из-за проверки типа, если вы хотите извлечь переменное число элементов, поскольку (a, b) и (a, b, c) имеют разные типы.

{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
tuple :: Int -> ExpQ
tuple n = do
    ns <- replicateM n (newName "x")
    lamE [foldr (\x y -> conP '(:) [varP x,y]) wildP ns] (tupE $ map varE ns)

Тогда:

$(tuple 6) [1,2,3,4,5,6] == (1,2,3,4,5,6)
$(tuple 3) "abc" == ('a','b','c')

Но в целом, если вам нужен этот ответ, тогда вы задаете неправильный вопрос где-то.

Если вам нужен только произвольный произвольный доступ, возможно, лучшим вариантом будет использование массива.

Ответ 3

Если чувствуешь, что я собираюсь советовать тебе указывать пистолет у твоей ноги и доверять тебе не стрелять.

> list2Tuple lst = read $ "(" ++ (init.tail.show) lst ++ ")"
> list2Tuple [1,2,3] :: (Int, Int, Int)
(1,2,3)
> list2Tuple [1,2,3,4] :: (Int, Int, Int, Int)
(1,2,3,4)

Это будет работать до того, что длина телеграммы Show и Read определена для.

Ответ 4

Кортежи и списки - это очень разные вещи. О том, что вы можете сделать, это вручную написать функцию преобразования:

toTuple :: [a] -> (a,a,a,a,a,a)
toTuple [a,b,c,d,e,f] = (a,b,c,d,e,f)

Обратите внимание, насколько различны типы: единственная переменная списка расширяется до шести переменных для кортежа. Таким образом, вам понадобится одна функция для каждого размера кортежа.

Ответ 5

Мне сложно сделать убедительные объяснения манипуляций с шаблоном Haskell, но вот демонстрация:

> :m +Language.Haskell.TH
> :set -XTemplateHaskell
> runQ [| [1,2,3,4,5,6] |] >>= putStrLn . pprint
[1, 2, 3, 4, 5, 6]
> runQ [| [1,2,3,4,5,6] |] >>= \ (ListE exps) -> putStrLn (pprint (TupE exps))
(1, 2, 3, 4, 5, 6)

Ответ 6

Я не думаю, что это можно сделать в Haskell, для списка произвольной длины, неизвестной во время компиляции. Шаблон Haskell не может этого сделать, потому что он работает только во время компиляции. Я столкнулся с ситуацией, когда мне нужно было сделать именно это, и мне пришлось обходиться. Библиотека базы данных ожидает кортежей различной длины для аргументов запроса, но у меня есть список произвольной длины. Поэтому я должен обойти библиотечный интерфейс. Было бы неплохо, если бы он мог взять список.

В основном проблема заключается в том, что кортежи различной длины - это разные типы. Но компилятор Haskell должен знать в компиляторе, какие типы могут существовать во время выполнения. Преобразование списка произвольной длины в кортеж во время выполнения может создать некоторый тип, о котором он не знал во время компиляции.

Ответ 7

На самом деле вы можете сделать лучше, чем вручную записать одну функцию в каждом размере, если вы используете квазикомиссию [1]. Тем не менее, я бы задался вопросом о коде, где вы ожидаете использовать это в целом.

[1] http://www.haskell.org/haskellwiki/Quasiquotation