2011-12-23 3 views
10

Я хочу, чтобы определить функцию, которая вычисляет количество элементов в списке, удовлетворяющих данный предикат:Количество элементов в Haskell в стиле pointfree

number_of_elements :: (a -> Bool) -> [a] -> Int 
    number_of_elements f xs = length (filter f xs) 

Например:

number_of_elements (==2) [2,1,54,1,2] 

должен возврат .

Мы можем написать его короче:

number_of_elements f = length . filter f 

Можно ли писать без ф параметра?

+3

Что ваше ищут называются «Pointfree стилем». Здесь есть wiki: http://www.haskell.org/haskellwiki/Pointfree. Он преподает вам все трюки, такие как сова: '((.) $ (.))' И точка: '((.). (.))'. Я бы лично не рекомендовал этот стиль. –

+6

Я бы порекомендовал немного поиграть с ним, чтобы посмотреть, как он работает, но с использованием частично точечного стиля 'number_of_elements f = length. фильтр f'. Это наиболее читаемое правило. –

+6

Это функция, которую я бы редко задавал, потому что 'length (filter f xs)', честно говоря, легче читать, чем 'number_of_elements f xs'. Последнее требует, чтобы я выяснил, что делает ваша функция, либо просматривая определение функции, документацию, либо вызывая ее из типа; в то время как первое представляет собой прямое совместное использование двух функций, которые я уже понимаю, - и это также короче, чтобы писать! Я бы только определил это как вспомогательную функцию в привязке 'where' или как функцию неэкспонированного модуля - и даже тогда, только если это будет аргумент для других функций. –

ответ

17

Конечно, это:

number_of_elements = (length .) . filter 
+17

Да, это правильный точечный, нечитаемый стиль :) –

+0

Спасибо, и вы можете объяснить, почему '(length. Filter)' или '(length $ filter)' не будет работать? – vikingsteve

+1

@vikingsteve, хорошо, 'длина. fitler' расширяется до '\ x -> length (filter x)'. Здесь 'filter x' является функцией, и вы не можете вычислить длину функции, что бы это ни значило. Аналогично, 'length $ filter' пытается вычислить длину функции' filter'. – Rotsor

12

Я не думаю, что вы можете получить более читаемым, чем то, что вы предложили. Однако, только для удовольствия вы можете сделать это:

numberOfElements = (.) (.) (.) length filter 

или

(.:) = (.) . (.) 
numberOfElements = length .: filter 
+3

'(. *)' Обычно называется '(. :)'; [lambdabot] (http://www.haskell.org/haskellwiki/Lambdabot) называет это, например. – ehird

+0

@ehird изменил его, спасибо. – is7s

+2

Почему бы не 'fmap fmap fmap'? –

7

Вы хотели бы прочитать о Semantic Editor Combinators. Возьмите result комбинатор оттуда:

result :: (output -> output') -> (input -> output) -> (input -> output') 
result = (.) 

result комбинатор принимает функцию и применяет его к результату другой функции. Теперь, глядя на функции мы имеем:

filter :: (a -> Bool) -> [a] -> [a] 
length :: [a] -> Int 

Теперь length относится к [a] «с; который, оказывается, является результатом типа функций вида foo :: [a] -> [a]. Так,

result length :: ([a] -> [a]) -> ([a] -> Int) 

Но результат filter оказывается в точности [a] -> [a] функции, поэтому мы хотим применить result length к результату filter:

number_of_elements = result (result length) filter 
+1

Я бы переписал эту последнюю строку, чтобы изолировать составленный SEC (семантический редактор combinator): 'number_of_elements = (result.result) length filter'. Читайте как: вычислите количество элементов, применяя 'length' к результату результата' filter'. Здесь '(result.result)' является комбинатором редактора, а 'length' является (семантическим) редактором. Определенно читайте сообщение [SEC post] (http://conal.net/blog/posts/semantic-editor-combinators). – Conal

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