Haskell получает позицию элемента в списке

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

lookUp :: [String] -> String -> Int
lookUp [] s = error "String no encontrado"
lookUp (x:xs) s | not(x == s) = 1 + (lookUp xs s)
                | otherwise = 0

Это выглядит довольно уродливым способом решения этой проблемы: используя отрицательную логику, errors, ... :(

Willem Van Onsem 10.08.2018 16:20

Задайте конкретный вопрос. Если каждая часть этого кода не имеет для вас смысла, этот вопрос, вероятно, слишком широк для StackOverflow.

melpomene 10.08.2018 16:21

Вы бы лучше поняли, если бы он был написан как lookup (x:xs) s = if x == s then 0 else (1 + lookup xs s)?

chepner 10.08.2018 16:21

Можете указать, чего вы в нем не понимаете? Синтаксис, типы, рекурсия, сопоставление с образцом?

Willem Van Onsem 10.08.2018 16:24

Прошу прощения, если я не стал вдаваться в подробности, как это было сделано ниже, я искал кого-нибудь, чтобы подробно описать каждую часть кода.

Javier Coronel 10.08.2018 19:42

@chepner Я пробовал по-твоему, это дает мне ошибку «тогда 0». Что случилось?

Javier Coronel 10.08.2018 20:16
0
6
1 346
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
lookUp :: [String] -> String -> Int
lookUp [] s = error "..."  -- An empty list does not contain _anything_, in
                           -- particular not the string you're looking for.
lookUp (x:xs) s     -- We've eliminated the empty case, which guarantees there's
                    -- at least one (head) element in the list if we get here.
                    -- Let's examine it!
   | not (x == s) -- If the element isn't the one we were looking for...
       = 1 + (lookUp xs s)   -- then we need to continue the search, i.e. search
                             -- through the _remaining_ elements `xs` and, if `s` is
                             -- found there, report a one higher position (because
                             -- we've already skipped one element).
   | otherwise  -- Else, the element _is_ the one we were looking for...
       = 0   -- IOW, it occurs at position 0 of the (part of) the list we're
             -- currently examining.

Еще пара замечаний:

  • Как прокомментировал Виллем Ван Онсем, error здесь - плохая идея: это реалистичный сценарий, когда в списке не будет элемента, который вы ищете, то есть это не просто «ой, удар метеорита сломал банкротрансор». «Вещь, но реальный риск, которого вы должны ожидать. Но error по умолчанию приведет к сбою всей программы. Вместо этого вы должны вернуть Maybe Int, который позволяет вам сигнализировать о сбое способом, который может легко обработать вызывающий.

    lookUp :: [String] -> String -> Maybe Int
    lookUp [] _ = Nothing
    lookUp (x:xs) s | not(x == s)  = fmap (1 +) (lookUp xs s)
                    | otherwise    = Just 0
    
  • На самом деле ничто в этой функции не требует, чтобы строки были в списке. Он также будет работать с целыми числами, одиночными символами, логическими значениями и т. д. Все, что позволяет проводить сравнение на равенство. Таким образом, вы можете также поставить подпись

    lookUp :: Eq a => [a] -> a -> Maybe Int
    

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

Javier Coronel 10.08.2018 19:39
lookUp :: [String] -> String -> Int

функция lookUp принимает список String и String, возвращающих Int

lookUp [] s = error "String no encontrado"

если первый аргумент - пустая строка, вернуть error ...

lookUp (x:xs) s | not(x == s) = 1 + (lookUp xs s)
                | otherwise = 0

интересная часть (x:xs) получает первую строку из списка, а строку | - это защита, поэтому если строка в x не равна строке sreturn 1 + ( lookup xs s) .. ==> рекурсивный вызов lookUp с xs - список строк без сравниваемой строки x и строки s в качестве параметра

в итоге othervise возвращает 0

вручную:

lookUp [] "foo" ==> первый шаблон [], поэтому возвращает ошибку

lookUp ["foo"] "foo" ==> второй шаблон и запускает охрану ==> not( "foo" == "foo") = 1 + ( lookUp [] "foo"), это заканчивается второй строкой othervise 0, поэтому он возвращает правильное местоположение 0

lookUp [ "bar", "foa", "foo", "fao" ] "foo" ==> второй шаблон и расширяется до: not ( "bar" == "foo") return 1 + (lookUp ["foa", "foo", "fao"] "foo"), затем not( "bar" == "foo") return 1 + (not ("foa" == "foo") = return 1 + (lookUp ["foo", "fao"] "foo")), затем not ("bar" == "foo") return 1 + (not ("foa" == "foo") = return 1 + (not ("foo" == "foo") = return 1) .. but because now test is *True* usesothervise = 0so1 + 1 = 2and2` - правильное расположение строки в списке.

и последняя возможность:

lookUp ["bar"] "foo" ==> not("bar" == "foo") = return 1 + (lookUp [] "foo") и lookUp с пустым списком выдает ошибку

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

Javier Coronel 10.08.2018 19:39

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