Новое в функциональном программировании

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

У меня проблема, когда я хочу обратиться к своему массиву, но я не думаю, что правильно понимаю типы. Или я создаю новый массив каждый раз, когда пытаюсь обратиться к нему - так же глупо. Посмотрите, пожалуйста, на myArray, te infix и dynamicProgram. Довольно красиво, пожалуйста

 Code


import Array
import Char

trans :: Int -> Int -> Double -> Double -> Double
trans from x trans11 trans21 =
    if (from == 1) && (x == 1)
        then trans11
    else if (from == 1) && (x == 2) 
        then (1-trans11)
    else if (from == 2) && (x == 1) 
        then trans21
    else (1-trans21)

em :: Char -> [Double] -> Double
em c list = list!! a
    where a = digitToInt c

intToChar :: Int -> Char
intToChar n | n == 1 = '1'
            | n == 2 = '2'

casino :: Char -> Int -> Int -> [Double] -> [Double] -> Double -> Double -> Double
casino seqchar 1 y em1 em2 t1 t2= 0.5 * (em seqchar em1)
casino seqchar 2 y em1 em2 t1 t2= 0.5 * (em seqchar em2)
casino seqchar x y em1 em2 t1 t2= maximum[ (1 @@ y-1)*(em seqchar em1)*(trans 1 x t1 t2),(2 @@ y-1)*(em seqchar em2)*(trans 2 x t1 t2) ]

dynamicProgram :: [Char] -> (Char -> Int -> Int -> [Double] -> [Double] -> Double -> Double -> Double) -> [Double] -> [Double] -> Double -> Double -> (Array a b)
dynamicProgram string score list1 list2 trans11 trans21 = myArray 1 len
                                [score (string!!y) x y list1 list2 trans11 trans21 | x  Int -> [Double] -> Array a b
myArray startIndex endIndex values = listArray (startIndex,startIndex) (endIndex,endIndex) values

traceback :: [Char] -> Int -> Int -> [Double] -> [Double] -> Double -> Double -> [Char]
traceback s 1 0 em1 em2 t1 t2 = []
traceback s 2 0 em1 em2 t1 t2 = []
traceback s x y em1 em2 t1 t2 | x@@y == (1 @@ y-1)*(em (s!!y) em1)*(trans 1 x t1 t2) = '1' : traceback s 1 (y-1) em1 em2 t1 t2
                            | x@@y == (2 @@ y-1)*(em (s!!y) em1)*(trans 2 x t1 t2) = '2' : traceback s 2 (y-1) em1 em2 t1 t2 

answer :: [Char] -> [Double] -> [Double] -> Double -> Double -> [Char]
answer string list1 list2 t1 t2 = reverse $ maxC : traceback string max end list1 list2 t1 t2 $ dynamicProgram casino string list1 list2 t1 t2
   where
      end = (length string) + 1
      max | maximum (1@@end) (2@@end) == 1@@end = 1
      | maximum (1@@end) (2@@end) == 2@@end = 2
      maxC = intToChar max

infix 5 @@
(@@) i j = myArray ! (i, j)

main = do
    putStrLn "What is the sequence to test?"
    seq  state 1 transmission probability?"
    trp1  state 2 transmission probability is " ++ (1-trp1)
    putStrLn "What is the state 2 -> state 1 transmission probability?"
    trp2  state 2 transmission probability is " ++ (1-trp2)
    putStrLn "I assume that the prob of starting in either state is 1/2.  Go!"
    answer seq st1 st2 trp1 trp2
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
1 344
2

Ответы 2

Я скопировал код из окна редактирования (что-то в парсере stackoverflow съедает часть кода) и попробовал его на ghci, который обнаружил несколько ошибок. Первая ошибка была:

foo.hs:34:71:
    Couldn't match expected type `[e]' against inferred type `(a, b)'
    In the second argument of `listArray', namely
        `(endIndex, endIndex)'
    In the expression:
        listArray (startIndex, startIndex) (endIndex, endIndex) values
    In the definition of `myArray':
        myArray startIndex endIndex values
                  = listArray (startIndex, startIndex) (endIndex, endIndex) values

Тип listArray:

listArray :: (Ix i) => (i, i) -> [e] -> Array i e
        -- Defined in GHC.Arr

Требуется кортеж с нижней и верхней границами и список. Итак, правильное выражение, вероятно, будет:

listArray (startIndex, endIndex) values

И тип myArray - это не Array a b, это Array Int Double.

Вторая ошибка была:

foo.hs:43:44:
    Couldn't match expected type `a -> b'
           against inferred type `[Char]'
    In the first argument of `($)', namely
        `maxC : (traceback string max end list1 list2 t1 t2)'
    In the second argument of `($)', namely
        `(maxC : (traceback string max end list1 list2 t1 t2))
       $ (dynamicProgram casino string list1 list2 t1 t2)'
    In the expression:
          reverse
        $ ((maxC : (traceback string max end list1 list2 t1 t2))
         $ (dynamicProgram casino string list1 list2 t1 t2))

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

Третья ошибка:

foo.hs:51:11:
    Couldn't match expected type `Array i e'
           against inferred type `Int -> Int -> [Double] -> Array a b'
    In the first argument of `(!)', namely `myArray'
    In the expression: myArray ! (i, j)
    In the definition of `@@': @@ i j = myArray ! (i, j)

myArray не является массивом; это функция, которая принимает три параметра и строит на их основе массив.

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

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

Последняя ошибка была:

foo.hs:63:4:
    Couldn't match expected type `[a]' against inferred type `IO ()'
    In the first argument of `(++)', namely
        `putStrLn
           "I assume that the state 1 -> state 2 transmission probability is "'
    In the expression:
          (putStrLn
             "I assume that the state 1 -> state 2 transmission probability is ")
        ++
          (1 - trp1)
    In a 'do' expression:
          (putStrLn
             "I assume that the state 1 -> state 2 transmission probability is ")
        ++
          (1 - trp1)

Это имеет две ошибки: та, на которую пожаловался компилятор, является проблемой приоритета, как ясно показывают добавленные компилятором круглые скобки, и которую можно легко исправить путем правильного применения скобок или оператора $. Другая ошибка, которую вы обнаружите после исправления этой ошибки, заключается в том, что вы не можете объединить строку и число; вам нужно преобразовать число в строку.

И все это происходило без просмотра алгоритма или даже большей части кода, а просто с учетом ошибок компилятора. Например, если вам нужен двумерный массив, правильным выражением для первой ошибки будет:

listArray ((startIndex, startIndex), (endIndex, endIndex)) values

Теперь обе границы являются кортежами, а его тип - Array (Int, Int) Double.

Вы можете переписать транс-функцию так:

trans :: Int -> Int -> Double -> Double -> Double
trans 1 1 trans11 trans21 =   trans11
trans 1 2 trans11 trans21 = 1-trans11
trans 2 1 trans11 trans21 =   trans21
trans _ _ trans11 trans21 = 1-trans21

Другие вопросы по теме