`

Haskell - Higher order functions

阅读更多

Haskell functions can take functions as parameters and return functions as return values. A function that does either of those is called a higher order function. Higher order functions aren't just a part of the Haskell experience, they pretty much are the Haskell experience. It turns out that if you want to define computations by defining what stuff is instead of defining steps that change some state and maybe looping them, higher order functions are indispensable. They're a really powerful way of solving problems and thinking about programs.

All the functions that accepted several parameters so far have been curried functions. , so we will examine the curried function. 

e.g.of the curried function is as such .

ghci> max 4 5  
5  
ghci> (max 4) 5  
5  

 Function application is  simply putting a space between two things. Let's takes the max function for example. 

max :: (Ord a) => a -> a -> a

  can be read as such 

max :: (Ord a) => a -> (a -> a)

 If we call a funcion with too few parameters, we get back a partially applied function. with this, we can create function on the fly.

we will examine the following.

  • curried function 
  • partially applied function 
  • the associativity of the function.
  • Maps and filters. 
  • Lambdas 
  • foldl, foldr
  • scanl, scanr
  • function application with $, $ being left-associative
  • function composition with dot (.)  (f . g)(x) = f(g(x))

Let's see the some examle on the topics above. 

 

-- curried_functions.hs
--  demonstrate the use of the high-order function and we first start with the curried functions


-- a note from the authro from http://learnyouahaskell.com/higher-order-functions
--   QUOTE:  It turns out that if you want to define computations by defining what stuff is instead of defining steps that change some state and maybe looping them, higher order functions are indispensable. 


-- 
-- :curried functions

-- first by an example.
-- max 4 5
-- (max 4) 5


-- type examination
--   max :: (Ord a) => a -> a -> a   --  
--   max :: (Ord a) => a -> (a -> a) -- a function that takes an a and returns a function that takes an a and returns an a.
-- more examples

multThree :: (Num a) => a -> a -> a -> a
multThree x y z = x * y * z


-- let multTwoWithNine = multThree 9
-- multTwoWithNine 2 3 

-- let multTwoWithEighteen = multThree 9
-- multTwoWithEighteen 10

compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred x = compare 100 x


-- and we can re-write this function above with the curried form 
compareWithHundred' :: (Num a, Ord a) => a -> Ordering
compareWithHundred' = compare 100



-- sections
--
-- infix function can also be applied by using sections
-- QUOTE : To section an infix function, simply surround it with parentheses and only supply a parameter on one side. That creates a function that takes one parameter and then applies it to the side that's missing an operand. .

divideByTen :: (Floating a ) => a -> a
divideByTen = (/10) -- the left operand is missing, so if you apply divideByTen 200, then argument '200' will be supplied to the missing left-operand

isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..'Z'])

subtract4 :: (Num a)  => a -> a
subtract4 =  (subtract 4) -- you cannot use (-4), because this means minus 4
-- substract4 10

-- 
-- : Some higher-orderism is in order
applyTwice :: (a -> a) -> a -> a -- two notes: first, '->' operator is right associative, and second, the parenthesis is mandatory, because that means the first argument is an function, and the second argument is of the same type and returns a value of the same type
applyTwice f x = f (f x)
-- applyTwice (+3) 10		   
-- applyTwice (++ " HAHA") "HEY"
-- applyTwice (multThree 2 2) 9
-- applyTwice (3:) [1]

-- impls of the zipWith function
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c] -- the first argument is an function, which accept two argument of type a and b respectively and returns a value of type 'c', 
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys

-- zipWith' (+) [4, 2, 5, 6] [ 2, 6, 2, 3]
-- zipWith' max [6,3,2,1] [7,3,1,5]
-- zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]  
-- zipWith' (*) (replicate 5 2) [1..] 
-- zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]  

-- impls of the flip' function, which flip the first two arguments
flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
  where g x y = f y x

--flip' zip [1, 2, 3, 4, 5] "hello"


--
-- :Maps and filters

-- map takes a function and a list and applies that function to every element in the list.   
map' :: (a -> b) -> [a] -> [b]
map' _ [] = []
map' f (x : xs) = f x : map' f xs


-- map (+3) [1, 5, 3, 1, 6]
-- map (++ "!") ["BIFF", "BANG", "POW"]
-- map (replicate 3) [3..6]
-- map (map (^2)) [[1,2],[3,4,5,6],[7,8]]  -- double applied map function to List[List]
-- map fst [(1,2),(3,5),(6,3),(2,6),(2,5)]

-- 
-- :filter
filter' :: (a -> Bool) -> [a] -> [a]
filter' _ [] = []
filter' p (x:xs) 
    | p x = x : filter' p xs
    | otherwise = filter' p xs
-- filter (>3) [1,5,3,2,1,6,4,3,2,1]      
-- filter (==3) [1,2,3,4,5]
-- filter (==3) [1,2,3,4,5]
-- let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]] 
-- filter (`elem` ['a' .. 'z'] "u LaUgH aT mE BeCaUsE I aM diFfeRent"  
-- filter (`elem` ['A'..'Z']) "i lauGh At You BecAuse u r aLL the Same"  	   
-- now let's redefine the quick sort function
quicksort :: (Ord a ) => [a] -> [a]
quicksort [] = []
quicksort (x : xs) = 
  let smallerSorted = quicksort (filter (<=x) xs)
      biggerSorted = quicksort (filter (>x) xs)
  in smallerSorted ++ [x] ++ biggerSorted
-- some performance NOTE: Thanks to Haskell's laziness, even if you map something over a list several times and filter it several times, it will only pass over the list once.  


-- e.g by case :  find the largest number under 10,000 that is divisible by 3829
largestDivisible :: (Integral a) => a
largestDivisible = head (filter p [10000, 9999..])
  where p x = x `mod` 3829 == 0

-- sum of of all odd squares that are smaller than 10,000.
-- sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
-- sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)]) -- it is a matter of taste

-- chain
chain :: (Integral a) => a -> [a]
chain 1 = [1]
chain n 
  | even n = n : chain(n `div` 2)
  | odd n = n : chain(n * 3 + 1)

numLongChains :: Int
numLongChains = length (filter isLong (map chain[1..100]))
  where isLong xs = length xs > 15

-- map to get a list of functions
-- let listOfFuns = map (*) [0..]
-- (listOfFuns !! 4) 5 -- !! is index operator


--
-- : Lambdas
-- lambdas are basically anonymous functions that are used because we need some functions only once.
-- QUOTE: we write a \ (because it kind of looks like the greek letter lambda if you squint hard enough) and then we write the parameters, separated by spaces. After that comes a -> and then the function body. We usually surround them by parentheses, because otherwise they extend all the way to the right. 

numLongChains' :: Int
numLongChains' = length (filter (\xs -> length xs > 15) (map chain [1..100]))

-- guideline of partially applied function vs. lambda expressions
-- use this
-- map (+3) [1, 6, 3, 2]
-- do NOT use this
-- map (\x -> x + 3) [1, 6, 3, 2]
-- e.g. of lambda expression 
-- zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]  

-- careful with pattern matching in lambda, because 
-- ONCE a pattern matching fails in a lambda, a runtime error occurs. 
-- map (\(a, b) -> a + b ) [(1,2),(3,5),(6,3),(2,6),(2,5)] 

-- normally by parenthesis, unless we mean for them to extend all the way to the right.
addThree :: (Num a) => a -> a -> a -> a
addThree x y z = x + y + z

addThree' :: (Num a) => a -> a -> a -> a
addThree' =  \x -> \y -> \z -> x + y + z -- not a perfect style of writing the fun

-- but depends on the times
flip'' :: (a -> b -> c) -> b -> a -> c
flip'' f = \x y -> f y x


-- 
-- : only folds and horses (topics on the foldLeft, foldRight?)
-- fold1, leftFold

sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs

-- sum' [3, 5, 2, 1]

-- re-impl of the `elem` operator again, but again with the fold1 funtion
elem' :: (Eq a) => a -> [a] -> Bool
elem' y ys = foldl (\acc x -> if x == y then True else acc) False ys

--
-- :: foldr
-- re-impl of the map' with foldr
map'r :: (a -> b) -> [a] -> [b]
map'r f xs = foldr (\x acc -> f x : acc) [] xs
-- a foldl impls is as follow, however, much more expensive. 
-- map'l :: (Num a) => [a] -> a
-- map'l f xs = foldl (\accx -> acc ++ [f x]) [] xs -- ++ operator is too expensive.

-- a review point of Folds is as follow 
-- QUOTE : Folds can be used to implement any function where you traverse a list once, element by element, and then return something based on that. Whenever you want to traverse a list to return something, chances are you want a fold. 

-- foldl1, and foldr1

maximum'' :: (Ord a) => [a] -> a  
maximum'' = foldr1 (\x acc -> if x > acc then x else acc)  
  
reverse'' :: [a] -> [a]  
reverse'' = foldl (\acc x -> x : acc) []  
  
product'' :: (Num a) => [a] -> a  
product'' = foldr1 (*)  
  
filter'' :: (a -> Bool) -> [a] -> [a]  
filter'' p = foldr (\x acc -> if p x then x : acc else acc) []  
  
head'' :: [a] -> a  
head'' = foldr1 (\x _ -> x)  
  
last'' :: [a] -> a  
last'' = foldl1 (\_ x -> x)  

-- scanl, scanr
-- like foldl, and foldr, but only report all the intermediate accumulator state in the form of a list. 
-- scanl (+) 0 [3, 5, 2, 1]
-- scanr (+) 0 [3, 5, 2, 1]
-- scanl1 (\acc x -> if x acc then x else acc) [3,4,5,3,7,9,2,1]
-- scanl (flip (:)) [] [3,2,1]
-- e.g. How many elements does it take for the sum of the roots of all natural numbers to exceed 1000? 

sqrtSums :: Int
sqrtSums = length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) + 1
-- verify that your code is right

-- sum (map sqrt [1..131])
-- sum (map sqrt [1..130])

-- 
-- : Function application with $
--   QUOTE : 
--     Alright, next up, we'll take a look at the $ function, also called function application. First of all, let's check out how it's defined:

-- the definition of the ($) operator
-- ($) :: (a -> b) -> a -> b
-- f $ x = f x

-- some facts about the '$' operator
--  1. normal function application (putting a space between two things) has a really high precedence, the $ function has the lowest precedence. 
--  2. Function application with a space is left-associative (so f a b c is the same as ((f a) b) c)), function application with $ is right-associative.

-- some e.g.
-- sum (map sqrt [1..130])
-- sum $ map sqrt [1..130]

-- sqrt $ 3 + 4 + 9
-- sqrt (3 + 4 + 9)

-- sum (filter (>10) (map (*2) [2..10]))
-- sum $ filter (>10) $ map (*2) [2..10]    


-- 
-- : Function composition
-- QUOTE: n mathematics, function composition is defined like thisn mathematics, function composition is defined like this : (f . g) (x) = f(g(x))
--  which means that composing two functions produces a new function that 
-- when called with parameter, say x is equivalent of calling g with the parameter x and then calling the f with that result
--
--  the haskell function composition 
--
-- the definition of the 'function composition'
-- (.) :: (b -> c) -> (a -> b) -> a -> c
-- f . g = \x -> f (g x)

-- e.g. of the function composition 
-- k = \x -> negate . (* 3) x
-- k = negate . (* 3) 

-- e.g. of use lambda    
-- map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
-- e.g of use function composition 
-- map (negate . abs) [5,-3,-6,7,-3,2,-19,24] 

-- map (\xs -> negate (sum (tail xs))) [[1..5],[3..6],[1..7]] 
-- map (negate . sum . tail) [[1..5],[3..6],[1..7]]  -- with composition style

-- replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))
-- replicate 100 . product . map (*3) . zipWith max [1,2,3,4,5] $ [4,5,6,7,8]    
-- because only with 'infix' operator, you can apply 'section' on those
-- infix operator (such as +, -, etc...) but you cannot use 
-- section operator 
-- 
-- map (negate $ sum $ tail)[[1..5],[3..6],[1..7]]

sum'pointer_style :: (Num a) => [a] -> a
sum'pointer_style xs = foldl (+) 0 xs

-- and the point-free styles is written as such 
-- 
sum'pointer_free_style :: (Num a) => [a] -> a
sum'pointer_free_style  = foldl (+) 0

-- the following is as follow. 
--
-- fn x= ceiling (negate (tan (cos (max 50 x))))
-- fn = ceiling . negate . tan . cos . max 50
-- fn = ceiling $ negate $ tan $ cos $ max 50  60
-- fn = ceiling $ negate $ tan $ cos $ max 50 $ 60

-- style of choice
-- which of the following style is more readable
oddSquareSum :: Integer
oddSquareSum = sum (takeWhile (<10000) (filter odd (map (^2) [1..])))

oddSquareSum' :: Integer
oddSquareSum' = sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]

oddSquareSum'' :: Integer
oddSquareSum'' = 
   let oddSquares = filter odd $ map (^ 2) [1..]
       belowLimit = takeWhile (<10000) oddSquares
   in sum belowLimit

 

分享到:
评论

相关推荐

    gmr-haskell-talk:数据类型练习的模板(haskell演讲)

    最后,高阶函数(Higher-Order Functions)允许我们传递函数作为参数或返回函数。例如,`map`函数接受一个函数和一个列表,将函数应用于列表的每个元素并返回新的列表: ```haskell map :: (a -&gt; b) -&gt; [a] -&gt; [b] ...

    haskell-semantics:Haskell 在 K 中的形式语义

    5. **高阶函数(Higher-Order Functions)**:Haskell 支持函数作为一等公民,可以作为参数传递,也可以作为返回值。这使得函数式编程风格更为自然。 6. **模式匹配(Pattern Matching)**:Haskell 的模式匹配允许...

    Haskell趣学指南---文字版.pdf

    第六章涉及高阶函数(Higher-order functions),这是函数式编程的核心特性之一。高阶函数是那些可以接受其他函数作为参数或返回函数作为结果的函数,Haskell中的Curried函数、map和filter、lambda表达式以及fold都是...

    Functional_Programming:练习以学习函数式编程

    - Higher Order Functions - Recursion - Currying - Partial Application - Lambdas 我将尽力解决这些问题。 尽管此处的大多数示例都将使用javascript进行简化和广泛采用,但我还将在其中提供一些示例 长生不老药 ...

    haskell最短路-自己实现fold代替递归

    Paul的第二次作业,Use higher-order functions such as map and fold (or adaptations thereof for relevant structures) rather than explicit recursion.也就是用自己针对Pathtree进行fold定义,然后利用fold来替换...

    personal-blog:我的博客是用爱和https制作的

    个人博客 例子 字形 图像 压缩率) 主题 〜/库/应用程序支持/高级文本2 ...avec-haskell / 709 / les-bases / 3293 / haskell-cest-quoi / http://learnyouahaskell.com/higher-order-functions https://www.fpcom

    learn-haskell:探索Haskell

    Higher-Order Functions(高阶函数)** Haskell中广泛使用高阶函数,它们可以接受函数作为参数,或者返回函数作为结果。例如,`map`、`filter`和`foldl`等函数是Haskell编程的基础。 **9. GHC(Glasgow Haskell ...

    WhyHaskellMatters:在本文中,我将通过展示Haskell的一些最重要和与众不同的功能,并通过可用的代码示例对其进行详细说明,以解释Haskell为何如此重要。 该演示文稿旨在成为独立的,不需要任何以前的语言知识

    4. **高阶函数(Higher-Order Functions)** - 高阶函数可以接受函数作为参数或返回函数。如 `map` 函数,它接受一个函数和一个列表,对列表中的每个元素应用该函数。 - 示例:`map (*2) [1, 2, 3]` 返回 `[2, 4, ...

    haskell_dsl_tour:在Haskell中实现DSL的相关技术示例

    3. **高阶函数(Higher-Order Functions)**:通过使用高阶函数,我们可以编写抽象的代码,这些代码接受其他函数作为参数,或者返回函数。这在创建DSL时非常有用,因为DSL经常需要定义一系列组合的步骤。 4. **模式...

    two-knights:Haskell国际象棋

    标签“Haskell”表明这个项目专注于学习和应用Haskell语言,因此,开发者可能会遇到类型系统的使用,如类型类(Type Classes)、模式匹配(Pattern Matching)、高阶函数(Higher-Order Functions)以及可能的Monads...

    functional-[removed]受 Haskell Prelude 模块启发,一些辅助函数

    3. **高阶函数(Higher-order functions)**:接受一个或多个函数作为参数,或者返回一个函数的函数。例如,`map`、`filter`和`reduce`都是高阶函数,它们可以操作数组并应用其他函数。 **underscore.js和lodash** ...

    俄罗斯方块:一个(不完整的)终端俄罗斯方块。 写在Haskell

    Haskell的模式匹配(Pattern Matching)和高阶函数(Higher-Order Functions)可以帮助我们简洁地实现这一功能,判断方块与游戏板的碰撞,以及方块落地后如何合并到游戏板上。 当方块落地或者填满一行时,得分计算...

    前端开源库-fp-ts

    3. **高阶函数(Higher-order functions)**:高阶函数可以接受一个或多个函数作为参数,或者返回一个函数。`fp-ts` 提供了 map、filter、reduce 等高阶函数,它们是函数式编程的核心工具。 4. **Monads**:Monad 是...

    movie-toolbox

    - 高阶函数(Higher-Order Functions):如 `map`、`filter` 和 `fold`,用于处理函数和数据结构。 - 递归:Haskell 中常用的数据处理方法。 - 解析库:如 `text` 和 `aeson`,用于解析和生成 JSON 数据。 - 并发与...

    tasty-grading-system

    【标签】"Haskell" 指出项目的核心语言是 Haskell,这暗示了该系统可能利用了 Haskell 的一些独特特性,比如类型类(Type Classes)、高阶函数(Higher-Order Functions)和模式匹配(Pattern Matching),以实现...

    哈斯克尔

    高阶函数(Higher-Order Functions)也是常见工具,它们接受函数作为参数或返回函数,增加了代码的灵活性和可重用性。 总的来说,哈斯克尔提供了一个严谨的环境,鼓励程序员编写简洁、可读性强的代码。它在理论和...

Global site tag (gtag.js) - Google Analytics