zip требует также второго списка, как она может работать только с одним списком аргументов

Я начал изучать Haskell и нашел хорошее упражнение. Это следующее:

grouping: Int -> [Student]->[(Team, Student)]
grouping teamNumber = zip ys
                      where ...

Итак, упражнение хочет, чтобы я попытался заполнить все остальное. Функция должна делать следующее: Пример: grouping 2 ['Mark','Hanna','Robert','Mike','Jimmy'] = [(1,'Mark'),(2,'Hanna'),(1,'Robert'),(2,'Mike'),(1,'Jimmy')].

Итак, мы формируем команды, состоящие из двух студентов, а у последнего студента «Джимми» нет товарищей по команде.

Затем я также смотрю, что делает предопределенная функция zip. Он получает два аргумента списка и соединяет каждый элемент списков с кортежем для создания списка кортежей.

Моя идея: 1) Я пытаюсь построить две функции "захват" и "бесконечность". Они выглядят следующим образом:

grap :: Int -> [a] -> [a]
grab _ [] = []
grab n (x:xs) = if n <= 0 then [] else x : grab (n-1) xs  

infinite :: Num a => a -> [a]
infinite x = x : infinite(x+1)

Итак, что они делают: с помощью infinite я хочу создать бесконечный список. И grap должно взять n элементов этого. Пример grap 2 (infinite 1) = [1,2].

Я использую эти два в первой строке моего объявления where для выполнения данной функции сверху. Так что я:

grouping: Int -> [Student]->[(Team, Student)]
grouping teamNumber = zip ys
                      where 
                      xs = grap teamNumber (infinite 1)

Итак, xs теперь мой первый список zip, особенно целочисленный список.

А теперь мой вопрос: zip как предопределенная функция требует также второго списка, особенно списка имен студентов, но в данной функции они дают zip только один аргумент, а именно ys как список. Как я могу это понять?


person user3097712    schedule 12.05.2014    source источник
comment
в вашем grouping вы пропустили ys = cycle xs. Кроме того, infinite == enumFrom , infinite 1 == [1..], grab == take , take n [1..] == [1..n].   -  person Will Ness    schedule 13.05.2014


Ответы (3)


Тип `группировки teamNumber`

Внимательно посмотрите на тип grouping :: Int -> [Student]->[(Team, Student)] и аргументы, которые объявляются для его объявления.

grouping :: Int        -> [Student]->[(Team, Student)]
grouping    teamNumber =  ...

Каков возвращаемый тип (тип справа от знака равенства), если grouping предоставляется со всеми аргументами, перечисленными слева от знака равенства?

Отвечать

Тип справа от знака равенства — [Student]->[(Team, Student)]. В Haskell функцию, которая принимает два аргумента и возвращает результат, можно эквивалентно рассматривать или определять как функцию, которая принимает первый аргумент и возвращает (функция, которая принимает второй аргумент и возвращает результат). Таким образом, мы могли бы сказать, например, что выражение

grouping 3 :: [Student]->[(Team, Student)]

(grouping 3) — это функция, которая принимает список студентов и возвращает список этих студентов, разбитых на 3 группы. Предположительно, если бы (grouping 3) было применено к списку студентов из вашего примера, мы бы имели

(grouping 3) [   'Mark' ,   'Hanna' ,   'Robert' ,   'Mike' ,   'Jimmy' ] =
             [(1,'Mark'),(2,'Hanna'),(3,'Robert'),(1,'Mike'),(2,'Jimmy')]

Тип `zip ys`

Какое отношение каррирование имеет к следующему типу и выражению?

zip :: [a] -> [b] -> [(a, b)]
zip    ys

Каким будет тип zip ys, если, например, ys :: [Bool]?

Какое это имеет отношение к вашему вопросу?

Когда вы рассматриваете это вместе с типом grouping teamNumber, как это говорит вам, какой тип ys должен быть в вашем упражнении?

Собираем все вместе

Из кода упражнения (игнорируя типы и предложение where) мы имеем:

grouping teamNumber = zip ys

Две вещи могут быть = в Haskell только в том случае, если их типы будут унифицированы. В этом случае тип grouping teamNumber должен унифицироваться с типом zip ys.

Из первой части мы знаем, что тип grouping teamNumber — это [Student]->[(Team,Student)].

Из второй части мы знаем, что zip ys имеет тип [b] -> [(a, b)], где a — это такой тип, что ys имеет тип [a].

Следовательно, мы знаем, что (~ — равенство типов в Haskell)

[Student]->[(Team,Student)] ~ [b] -> [(a, b)]

Они будут унифицированы, если мы заменим следующие переменные типа b и a

b ~ Student
a ~ Team

Теперь мы знаем, что тип ys — это [a], который, если мы сделаем ту же замену, будет [Team].

Следовательно, типы будут правильными, если ys :: [Team].

Вывод

Если вы можете предоставить ys :: [Team], вы можете создать функцию от студентов к студентам, помеченным их командой ([Student]->[(Team,Student)]), передав ys в качестве первого аргумента zip. Именно такая функция должна возвращать grouping, когда она применяется к единственному аргументу teamNumber :: Int.

person Cirdec    schedule 12.05.2014
comment
Тип справа от знака равенства должен быть ([Студент], [(Команда, Студент)]), верно? - person user3097712; 12.05.2014
comment
Нет, это не кортеж, содержащий как список студентов, так и список кортежей команд и студентов. Это функция, которая принимает список студентов и возвращает список кортежей команд и студентов. [Student]->[(Team, Student)]. В Haskell функцию, которая принимает два аргумента и возвращает результат, можно эквивалентно рассматривать или определять как функцию, которая принимает первый аргумент и возвращает (функция, которая принимает второй аргумент и возвращает результат). В стороне: это называется карри и названо в честь Хаскелла Карри. - person Cirdec; 12.05.2014
comment
Чтобы ответить на ваш первый вопрос, я могу сказать, что тип zip ys должен быть [b] -> [(Bool, b)]. Но для других я должен подумать об этом - person user3097712; 12.05.2014
comment
[b] -> [(Bool, b)] похоже на что-то еще, что вам нужно? Возможно, если вместо Bool попробовать что-то другое? - person Cirdec; 12.05.2014
comment
я думаю, мне следует прочитать дополнительную информацию о каррировании, прежде чем я продолжу, потому что в данный момент я очень запутался. - person user3097712; 13.05.2014
comment
Цирдек, Дфойер и Уилл Несс. спасибо за ваши объяснения. Это очень помогает мне понять упражнение. Думаю, теперь я готов решить проблему. благодарю вас. - person user3097712; 13.05.2014

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

Основная концепция такова: в Haskell каждая функция принимает только один аргумент. Если вы хотите имитировать функцию, которая принимает два аргумента, есть два способа сделать это:

Кортежи

Вы можете написать функцию, которая принимает кортеж. Это традиционный подход в Standard ML, но обычно он используется в Haskell только в тех случаях, когда это особенно разумно:

distanceFromOrigin :: (Double, Double) -> Double
distanceFromOrigin (x, y) = sqrt (x^2 + y^2)

карри

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

product :: Double -> (Double -> Double)
product x = \y -> x * y

Предположим, я начинаю с (product 3) 4. Я могу сначала уменьшить (product 3), чтобы получить

(\y -> 3 * y) 4

Тогда я могу закончить работу, получив 12.

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

product x y = x * y

значить

product x = \y -> x * y

Во-вторых, это делает применение функции левоассоциативным, поэтому я могу написать product 3 4, чтобы означать (product 3) 4.

Наконец, это делает конструктор типа -> правоассоциативным, так что я могу написать product :: Double -> Double -> Double вместо product :: Double -> (Double -> Double).

person dfeuer    schedule 13.05.2014

В Haskell следующее эквивалентно:

f = (\x      y -> ..x..y..  )
f = (\x -> (\y -> ..x..y.. ))  -- this equivalence is known as "currying"
f     x =  (\y -> ..x..y.. )   -- partially applying f with x gives (\y->...)
f     x      y =  ..x..y..

((\x -> ...) — это, конечно, нотация Haskell для анонимных, так называемых «лямбда» функций (\ — это напоминание о греческой букве λ.)

В Haskell функции такие же, как и другие значения, поэтому нет специального синтаксиса для вызовов функций или «указателей функций» и т. д. Что касается типов, вышеизложенное, естественно, влечет за собой

f ::  a ->   b ->   t
f     x ::   b ->   t  -- the result of calling f w/ x (of type a) has type b->t
f     x      y ::   t  -- when f :: a->b->t, x :: a, y :: b, then f x y :: t

Посмотрите на это на мгновение.

Так вот о карри. Вызовы функций в Haskell обозначаются просто сопоставлением, поэтому они ассоциируются слева (f x y на самом деле ((f x) y)). А поскольку определения Haskell автоматически каррируются, стрелки в типах ассоциируются справа (a->b->c на самом деле a->(b->c)).

person Will Ness    schedule 13.05.2014