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

Номера и типы Haskell?

У меня есть этот код Javascript:

N1 = Math.floor(275 * month / 9)
N2 = Math.floor((month + 9) / 12)
N3 = (1 + Math.floor((year - 4 * Math.floor(year / 4) + 2) / 3))
N = N1 - (N2 * N3) + day - 30
return N

Я попытался передать это в Haskell. Вот так:

day_of_year year month day = n1 - (n2 * n3) + day - 30
  where
    n1 = floor(275 * fromIntegral month / 9)
    n2 = floor( month + 9 / 12)
    n3 =  1 +  floor((year - 4 * floor(fromIntegral year / 4) + 2) / 3)

Это не работает:(  Вот мои вопросы:

  • Почему тип n1 написан как n1 :: (Integral b, RealFrac a) => a -> b
    но не нравится n1 :: (RealFrac a, Integral b) => a -> b
    То же самое с floor :: (Integral b, RealFrac a) => a -> b

    Ответ: заказ не имеет значения в левой части = >
     ghci, как правило, попытается сохранить порядок так же, как и порядок в декларации
     но иногда по умолчанию выполняется abc-упорядочение

  • Правильно ли это утверждение: n1 принимает целочисленное число и возвращает RealFrac.

    Ответ: Да. Если мы знаем, что упорядочение неважно с левой стороны = >
     то мы также знаем, что (Интеграл b, RealFrac a) === (RealFrac a, Интеграл b)
     что важны только типы a → b
     или в этом случае Интеграл → RealFrac

  • n3 имеют болезнь Мономорфизма. Как его можно вылечить?
    Меня больше интересует большая картина, чем просто работа над этим. Я читал о моно... но я понятия не имею, куда положить:: в этом случае: (

    Ответ: Здесь нет мономорфизма. Посмотрите на FUZxxl ответ:)

  • Может day_of_year быть следующим: Integral -> Integral -> Integral -> Integral?
    Принимает 3 интеграла и возвращает интегральный результат.

    Ответ: Да, это возможно! Он также может быть  :: Интеграл a = > a → a → a → a
     :: Int → Int → Int → - > Int
     :: (Интеграл a, Интеграл a2, Интеграл a1) = > a → a1 → a2 → a2

  • Я полагаю, что day_of_year может принимать только 3 Ints или 3 целых числа. Он не мог принять микс, как 2 Ints 1 integer. Правильно?

    FUZxxl: Нет, он может принимать разные типы аргументов! Посмотрите на продолжение 4!!!

  • Можно ли создать day_of_year, чтобы взять 3 Nums и вернуть Num?

    FUZxxl: Да, это так! Поместите aEnum в течение года, месяца и дня.

4b9b3361

Ответ 1

Хорошо. Всякий раз, когда у вас есть проблемы с типом, это лучший способ начать с предоставления явным аннотациям типа компилятору. Поскольку day, month и year, вероятно, не слишком большие, рекомендуется сделать их Int s. Вы также явно пропустили скобу, я исправил это для вас:

day_of_year :: Int -> Int -> Int -> Int
day_of_year year month day = n1 - (n2 * n3) + day - 30
  where
    n1 = floor(275 * fromIntegral month / 9)
    n2 = floor((month + 9) / 12)
    n3 =  1 +  floor((year - 4 * floor(fromIntegral year / 4) + 2) / 3)

Когда я пытаюсь скомпилировать это, GHC выплескивает это довольно длинное сообщение об ошибке:

bar.hs:8:16:
    No instance for (RealFrac Int)
      arising from a use of `floor'
    Possible fix: add an instance declaration for (RealFrac Int)
    In the second argument of `(+)', namely
      `floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)'
    In the expression:
      1 + floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)
    In an equation for `n3':
        n3 = 1 + floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)

bar.hs:8:68:
    No instance for (Fractional Int)
      arising from a use of `/'
    Possible fix: add an instance declaration for (Fractional Int)
    In the first argument of `floor', namely
      `((year - 4 * floor (fromIntegral year / 4) + 2) / 3)'
    In the second argument of `(+)', namely
      `floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)'
    In the expression:
      1 + floor ((year - 4 * floor (fromIntegral year / 4) + 2) / 3)

Вторая ошибка - важная ошибка, первая - продолжение. По существу, он говорит: Int не выполняет деление не floor. В Haskell интегральное деление использует другую функцию (div или quot), но здесь вы хотите иметь плавающее деление. Поскольку year закрепляется как Int, вычитаемый 4 * floor(fromIntegral year / 4) + 2 также закрепляется как Int. Затем вы делитесь на 3, но, как говорилось ранее, вы не можете использовать плавающее подразделение. Пусть исправить это, "отбрасывая" весь термин другому типу с помощью fromIntegral перед делением (как и раньше).

fromIntegral имеет подпись (Integral a, Num b) => a -> b. Это означает: fromIntegral принимает переменную интегрального типа (например, Int или Integer) и возвращает переменную любого числового типа.

Попробуйте скомпилировать обновленный код. Аналогичная ошибка появляется при определении n2, я также исправил ее:

day_of_year :: Int -> Int -> Int -> Int
day_of_year year month day = n1 - (n2 * n3) + day - 30
  where
    n1 = floor(275 * fromIntegral month / 9)
    n2 = floor((fromIntegral month + 9) / 12)
    n3 =  1 +  floor(fromIntegral (year - 4 * floor(fromIntegral year / 4) + 2) / 3)

Этот код компилируется и работает отлично (на моей машине). Haskell имеет определенные правила по умолчанию, заставляя компилятор выбрать Double как тип для всех плавающих делений.

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

day_of_year :: Int -> Int -> Int -> Int
day_of_year year month day = n1 - (n2 * n3) + day - 30
  where
    n1 = 275 * month `quot` 9
    n2 = (month + 9) `quot` 12
    n3 = 1 + (year - 4 * (year `quot` 4) + 2) `quot` 3

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

О вашей шестой точке: Да, было бы довольно легко сделать это. Просто поставьте fromEnum перед year, month и day. Функция fromEnum :: Enum a => a -> Int преобразует любой тип перечисления в Int. Все доступные числовые типы в Haskell (кроме сложных iirc) являются членами класса Enum. Это не очень хорошая идея, хотя, как правило, у вас есть аргументы Int, а лишние вызовы функций могут замедлить работу вашей программы. Улучшите преобразование явно, за исключением случаев, когда предполагается, что ваша функция будет использоваться со многими различными типами. Собственно, не беспокойтесь о микро-оптимизации слишком много. ghc имеет сложную и несколько скрытую инфраструктуру оптимизации, которая ускоряет работу большинства программ.

Изменение

Последующие действия 1, 2 и 3

Да, ваши рассуждения верны.

Последующее наблюдение 4

Если вы не даете вариант с плавающей запятой day_of_year подписи типа, его тип по умолчанию равен day_of_year :: (Integral a, Integral a2, Integral a1) => a -> a1 -> a2 -> a2. Это по существу означает: day, month и year могут быть произвольного типа, который реализует класс Integral. Функция возвращает значение того же типа, что и day. В этом случае a, a1 и a2 - это просто переменные переменные типа - да, Haskell также имеет переменные на уровне типа (а также на уровне вида [который является типом типа], но эта другая история ) - это может быть выполнено любым типом. Поэтому, если у вас есть

day_of_year (2012 :: Int16) (5 :: Int8) (1 :: Integer)

Переменная a получает instaniated до Int16, a1 становится Int8 и a2 становится Integer. Итак, какой тип возврата в этом случае?

Это Integer, посмотрите на подпись типа!

Последующее наблюдение 5

Фактически вы являетесь и не являетесь одновременно. Создание типа как можно более общего, безусловно, имеет свои преимущества, но при этом он смущает typechecker, потому что, когда типы, связанные с термином без явной аннотации типа, являются слишком общими, компилятор может обнаружить, что существует более одного возможного тип для срока. Это может привести к тому, что компилятор может выбрать тип с помощью некоторых стандартизированных, хотя и несколько неинтуитивных правил, или просто приветствует вас странной ошибкой.

Если вам действительно нужен общий тип, старайтесь что-то вроде

day_of_year :: Integral a => a -> a -> a -> a

То есть: аргументы могут иметь произвольный тип Integral, но все аргументы должны иметь один и тот же тип.

Всегда помните, что Haskell никогда не применяет типы. Практически невозможно полностью выводить типы, когда есть (автоматическое) литье. Вы выполняете только вручную. Некоторые люди могут теперь рассказать вам о функции unsafeCoerce в модуле Unsafe.Coerce, который имеет тип a -> b, но вы на самом деле не хотите знать. Вероятно, это не так, как вы думаете.

Последующее наблюдение 6

В div нет ничего плохого. Разница начинает появляться, когда задействованы отрицательные числа. Современные процессоры (например, Intel, AMD и ARM) реализуют quot и rem в оборудовании. div также использует эти операции, но делает некоторые трюки, чтобы получить другое поведение. Это лишний раз замедляет вычисления, когда вы действительно не зависите от точного поведения относительно отрицательных чисел. (На самом деле есть несколько машин, которые реализуют div, но не quot в аппаратном обеспечении. Единственное, что я могу запомнить сейчас, - , хотя)

Ответ 2

У меня слишком много последующих вопросов для простого комментария.

  • n1 очевидно. fromIntegral принимает month и отбрасывает его в некоторый тип, относящийся к /.
    Я здесь?

    Да.

  • Но в n2 мы можем предположить, fromIntegral(month + 9) === (fromIntegral month + 9)

    • В первом случае month и 9 добавляются, а затем добавляются к некоторому типу для / Это работает, потому что + находится в Num, поэтому каждое число может быть + без кастинга. И необработанные числа, такие как 1,2,3, также имеют тип Num.
    • Во втором случае есть своего рода "отсроченное кастинг". (fromIntegral month + 9) имеют тип Num a => a, но из-за /12 компилятор отличает month AND 9 к некоторому типу, совместимому с /.
      Правильно ли я понял?

      ! Да.

  • FUZxxl, мужчина, спасибо!
    Я был так близок к решению этого, переделывая код и помещая fromIntegral наугад.
    Но сделать работу с кодом не так, как знать, почему мы что-то делаем!

    • Оба floor AND / не допускаются для Integral!
    • n3, вторая переменная year: Используя floor(fromIntegral year / 4), я убедительно сделал результат, который мог бы использовать floor. И это выражение произвело целую (year - 4 * floor(fromIntegral year / 4) + 2) a Integral typeclass!
      Таким образом, невозможно выполнить /3 и первый floor.
      Моя логика в порядке?

      ! Да.

  • Ваши печатные работы: day_of_year :: Int -> Int -> Int -> Int
    Моя печать также работает: day_of_year :: Integral a => a -> a -> a -> a
    Авто-типизация: day_of_year :: (Integral a, Integral a2, Integral a1) => a -> a1 -> a2 -> a2
    Что это значит? Что такое a1, a2? Почему a, a1, a2?

    Удивительный ответ в последующем 4

  • Я делаю ошибку здесь, пытаясь создать общую функцию, которая принимает Integral вместо конкретных Int или Integer?

    • В JavaScript все автозаполняется/динамически вводится в/из типа Number.
    • В С++ есть templates, поэтому универсальная функция работает на многих типах.

      ! Посмотрите на последующие 5

  • Почему вы использовали quot вместо div?
    Вчера я пробовал точно, что вы сделали с div и ghci, наградил меня:
    No instance for (Integral (Car -> Int)) arising from a use of div

    • Что это за ошибка?
    • Какая разница между div и quot в этом случае?

      ! Я случайно удалил год и месяц из определения функции
      ! day_of_year day =...
      ! и эта ошибка появилась.

p.s. Google: для haskell ничего не найдено "(Integral (Car → Int))"

Даже Google не может найти мои опечатки;)))