функция высшего порядка haskell - PullRequest
0 голосов
/ 07 июня 2018

Я новичок в Haskell, я должен сделать функцию, которая подсчитывает количество гласных в строке, используя функцию высшего порядка foldr

Я пытался создатьэта функция

vowels [] = 0
vowels (x:xs)= if elem x "aeiou" then 1 + vowels xs else vowels xs

Но она не работает, и я не могу сделать это, используя foldr, любое предложение?

Ответы [ 4 ]

0 голосов
/ 07 июня 2018

Ваше определение может быть изменено на

vowels []     = 0
vowels (x:xs) = g x (vowels xs)
  where
  g x rec = if elem x "aeiou" then 1 + rec else rec

, что соответствует шаблону

foldr r z [] = z
foldr r z (x:xs) = r x (foldr r z xs)

, если у нас есть foldr r z = vowels и r = g, а также z = 0.

Этот "шаблон" на самом деле является действительным определением функции foldr.

Таким образом, мы действительно имеем

vowels xs = foldr g 0 xs
  where
  g x rec = if elem x "aeiou" then 1 + rec else rec
0 голосов
/ 07 июня 2018

Вам нужно взглянуть на подпись foldr.

foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b

Не берите в голову часть Foldable и сосредоточьтесь на первой выполняемой функции.(a -> b -> b) b - это тот же тип, который вы должны возвращать, поэтому прямой перевод подписи в лямбду дает вам \x acc -> acc, но вы хотите сделать больше, чем просто игнорировать каждый элемент.

Взгляните на свою функцию if elem x "aeiou" then 1 + vowels xs else vowels xs.Вам нужно вернуть b, а не повторять, добавив один к нему

if elem x "aeiou" эта часть в порядке.then 1 + acc <- видите, что я здесь делаю?Я добавляю один к аккумулятору, не повторяя вручную, что делается <code>foldr, как для случая else: acc.Вот и все.Вам не нужно даже нажимать x.

Собирая все вместе: vowels = foldr (\x acc -> if elem x "aeiou" then 1 + acc else acc) 0 0 - это то, с чего acc будет начинаться.

Если вы хотитеузнать больше о складках, я предлагаю вам самим их переопределить.

0 голосов
/ 07 июня 2018

Самый простой способ написать что-то подобное - позволить компилятору вести вас.

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

foldr :: (a -> b -> b)  -- ^ Not obvious
      -> b              -- ^ Not obvious
      -> [a]            -- ^ A list... that'll be the input string
      -> b              -- ^ Final result, so nothing to be done here.

Итак, ваша реализация будет иметь вид

vowels :: String -> Int
vowels s = foldr _ _ s

, где нам еще нужно выяснить, что положить в _ пробелы.Компилятор даст вам полезные советы:

$ ghc wtmpf-file6869.hs 
[1 of 1] Compiling Main             ( wtmpf-file6869.hs, wtmpf-file6869.o )

/tmp/wtmpf-file6869.hs:2:18: error:
    • Found hole: _ :: Char -> Int -> Int
    • In the first argument of ‘foldr’, namely ‘_’
      In the expression: foldr _ _ s
      In an equation for ‘Main.vowels’: Main.vowels s = foldr _ _ s
    • Relevant bindings include
        s :: String (bound at /tmp/wtmpf-file6869.hs:2:8)
        vowels :: String -> Int (bound at /tmp/wtmpf-file6869.hs:2:1)
  |
2 | vowels s = foldr _ _ s
  |                  ^

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

vowels (<b>x</b>:xs) = <b>if elem x "aeiou" then 1 + </b>vowels xs else vowels xs

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

vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s

Мне пришлось поставить 1+ в скобках, чтобы он работал без явного аргумента, как секция оператора .

Хорошо, больше пробелов:

    • Found hole: _ :: Int -> Int
    • In the expression: _
      In the expression: if x `elem` "aeiou" then (1 +) else _
      In the first argument of ‘foldr’, namely
        ‘(\ x -> if x `elem` "aeiou" then (1 +) else _)’
    • Relevant bindings include
        x :: Char (bound at wtmpf-file6869.hs:2:20)
        s :: String (bound at wtmpf-file6869.hs:2:8)
        vowels :: String -> Int (bound at wtmpf-file6869.hs:2:1)
  |
2 | vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else _) _ s
  |                                                          ^

Так что это модификатор, который должен действовать, когда вы нашли не гласный.Что вы хотите изменить в этом случае?Ну, на самом деле ничего: счет должен оставаться как есть.Это достигается с помощью функции id.

vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) _ s
    • Found hole: _ :: Int
    • In the second argument of ‘foldr’, namely ‘_’
      In the expression:
        foldr (\ x -> if x `elem` "aeiou" then (1 +) else id) _ s
      In an equation for ‘vowels’:
          vowels s
            = foldr (\ x -> if x `elem` "aeiou" then (1 +) else id) _ s
    • Relevant bindings include
        s :: String (bound at wtmpf-file6869.hs:2:8)
        vowels :: String -> Int (bound at wtmpf-file6869.hs:2:1)
  |
2 | vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) _ s
  |                                                              ^

Так что это целое число, которое полностью выходит за пределы foldr.Т.е. это не может зависеть от строки.В частности, он также будет использоваться, если строка пуста .Может быть только 0!

vowels s = foldr (\x -> if x`elem`"aeiou" then (1+) else id) 0 s

Пробелов больше нет, поэтому компилятор просто примет это.Проверьте это:

$ ghci wtmpf-file6869
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Loaded GHCi configuration from /home/sagemuej/.ghci
[1 of 1] Compiling Main             ( wtmpf-file6869.hs, interpreted )
Ok, 1 module loaded.
*Main> vowels "uwkaefdohinurheoi"
9
0 голосов
/ 07 июня 2018

Скважина a foldr :: (a -> b -> b) -> b -> [a] -> b - это функция, где первым параметром является функция f :: a -> b -> b.Здесь вы можете видеть параметр a как " head " списка, второй параметр b как результат рекурсии с foldr, и таким образом выхочу получить результат в терминах этих двух для всей функции.Эта логика в основном заключена во втором разделе вашей функции.

Действительно:

vowels (x:xs) = if elem x "aeiou" then 1 + vowels xs else vowels xs

можно переписать так:

vowels (x:xs) = if elem x "aeiou" then 1 + <b>rec</b> else <b>rec</b>
    where <b>rec = vowels xs</b>

и, таким образом, recрезультат рекурсивного вызова, второй параметр «сложения» -функции.x, с другой стороны, является первым параметром фолд-функции.Таким образом, нам нужно написать эту функцию, только в терминах x и rec, и это просто:

\x rec -> if elem x "aeiou" then 1 + <b>rec</b> else <b>rec</b>

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

vowels = foldr (\x rec -> if elem x "aeiou" then 1 + <b>rec</b> else <b>rec</b>) 0

Или более чистый синтаксис:

vowels = foldr <b>f</b> 0
    where f x rec | elem x "aeiou" = 1 + rec
                  | otherwise = rec

Мыможно дополнительно очистить его, абстрагировавшись от rec:

vowels = foldr <b>f</b> 0
    where f x | elem x "aeiou" = <b>(1+)</b>
              | otherwise = <b>id</b>
...