2013-11-09 5 views
2

Ну, я делал проблему, в которой функция, которую я использовал, имела жесткую переменную. У меня была идея использовать массивы для этой проблемы. Поэтому я думал об использовании массивов с той же жесткой переменной, что и о создаваемой мне функции, но я понятия не имею, как создать массив с жесткой переменной. Я попробовал следующее вещь, но без эффекта:Массивы с жесткой переменной

rearrange :: [Int] -> [a] -> [a] 

rearrange l la = elems (f 1 posarr)  
    where 
    b = length l 
    listarr :: Array Int Int 
    listarr = listArray (1,b) l 
    arra :: Array Int c 
    arra = listArray (1,b) la 
    posarr :: Array Int c 
    posarr = listArray (1,b) la 
    f i posarr 
     | (b < i) = posarr 
     | otherwise = f (i+1) (posarr // [(listarr!i,arra!i)]) 

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

ответ

5

У вас возникли проблемы, потому что написанные вами «внутренние» типы подписей не означают, что они выглядят как бы это значило. В частности, ваше использование c не соответствует друг другу, а не a в верхней подписи, и они должны. Haskell жалуется, что эти «жестко определенные» переменные типа, несмотря на переменные, не могут быть одинаковыми, но поскольку они основаны на «жестком» выборе того, что a ... они должны!

Вы можете сделать GHC (но не Haskell в целом) ведут себя так, как вы хотите с расширением под названием {-# LANGUAGE ScopedTypeVariables #-}, где ваш код становится

{-# LANGUAGE ScopedTypeVariables #-} 

rearrange :: forall a . [Int] -> [a] -> [a] 
rearrange l la = elems (f 1 posarr) 
    where 
    b = length l 
    listarr :: Array Int Int 
    listarr = listArray (1, b) l 
    arra :: Array Int a 
    arra = listArray (1,b) la 
    posarr :: Array Int a 
    posarr listArray (1,b) la 
    f i posarr 
     | (b < i) = posarr 
     | otherwise = f (i+1) (posarr // [(listarr!i,arra!i)]) 

Обратите внимание, что все, что я сделал добавить явный forall и изменено некоторые c переменные до a. Что делать ScopedTypeVariables позволяет вводить области переменных типа с помощью forall, где любые подписи типов в коде, которые отступы под таким явным знаком forall 'd, могут повторно использовать имена переменных типа, введенные в этом forall, и иметь их в точности.

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

        -- is actually 
foo :: [a] -> [a] -> [a]   foo :: forall a. [a] -> [a] -> [a] 
foo xs ys = it where    foo xs ys = it where 
    it :: [a]       it :: forall a. [a] 
    it = xs ++ ys      it = xs ++ ys 

Что заставляет a переменной в каждой из этих подписей типа быть разными и, таким образом, этот фрагмент кода не может компилироваться, так как он действует только тогда, когда те, два a s одинаковы. С ScopedTypeVariables мы имеем

foo :: forall a . [a] -> [a] -> [a] 
foo xs ys = it where 
    it :: [a] 
    it = xs ++ ys 

, где внутренняя область видимости сигнатура-х a означает точно такой же, как и в a внешней подписи х.

+0

Является ли следующий код компиляцией без ошибок. я сделал то же самое, что и вы, но это говорит что-то вроде этого: «Незаконный символ». в типе Возможно, вы указали -XRanktype или аналогичный флаг, чтобы включить явный forall ........ Но одна вещь, которую я использую Ubuntu и компиляция с использованием терминала, также вызывает некоторую ошибку. – supernova

+0

Чтобы включить 'forall', вы должны использовать расширение' ScopedTypeVariables'. 'RankNTypes' - это совершенно другое расширение. Оба они зависят от и автоматически помещают расширение «ExplicitForalls», которое вы также можете выбрать отдельно, если хотите, хотя оно не имеет семантических преимуществ. –

+0

Если вы используете GHCi, вы можете установить эти флаги на лету, как ': set -XScopedTypeVariables' –

4

J. Abrahamson объяснил, как исправить ошибки под рукой, но обратите внимание, что это слишком сложно. Вы можете просто опустить все локальные типы подписей , компилятор может сделать это сам. (Там достаточно уверенный являются приложений, где необходимы локальные сигнатуры типов, или там, где это полезно для удобства чтения, то вам часто ScopedTypeVariables Но не такой простой функции, ИМО.).

Говоря ненужную сложность - есть нет никакой пользы в явном индексировании с помощью массива, который вы только что создали, с помощью listArray: он почти эквивалентен (но значительно неудобен) для рекурсивного уничтожения списка. И это можно записать как складку. Или, в таких случаях, когда вы перебираете два списка «параллельно», сворачиваете на zip этих списков.На самом деле вам не нужна даже сводка: есть веская причина, что (//) принимает список пар index-value, а не только одну пару - потому что вы обычно хотите обновлять несколько элементов в партии.

Это упрощает функцию резко:

rearrange l la = elems $ posarr // zip l la 
    where posarr = listArray (1, length l) la 

Так я не понял: я действительно имею в виду только местные подписи типа. На верхнем уровне все должно иметь подпись, кроме, возможно, совершенно тривиального материала, который используется только в вашем модуле (но тогда он также является локальной подписью).

Существует больше к этому, чем просто удобство: ваше решение на самом деле очень неэффективно, потому что на каждую стадии модификации копия всего массива должна быть сделана, так что вы можете безопасно получить доступ к промежуточным результатам чисто функциональный язык. Вызов // с несколькими парами сразу пропустил это: поскольку промежуточные шаги никогда не отображаются, GHC может делать магию под капотом, делать деструктивные обновления, как вы могли на императивном языке (или в Haskell с монадой ST). Это намного быстрее.

+1

Для справки это гораздо лучшее предложение, чем мое, чтобы улучшить общее качество кода. –

+0

Спасибо за помощь. Можете ли вы сказать мне, что для каждого обновления в массиве требуется порядок порядка размера n длины списка? Таким образом, для n обновления требуется порядок шагов $ n^2 $. – supernova

+0

Да, это точно проблема с вашим подходом. Для каждого _секретного обновления_; как я сказал, '//' их обманывает, чтобы избежать этой проблемы. – leftaroundabout

Смежные вопросы