原文地址:Haskell学习-functor

什么是Functor

functor 就是可以执行map操作的对象,functor就像是附加了语义的表达式,可以用盒子进行比喻。functor 的定义可以这样理解:给出a映射到b的函数和装了a的盒子,结果会返回装了b的盒子。fmap 可以看作是一个接受一个function 和一个 functor 的函数,它把function 应用到 functor 的每一个元素(映射)。

-- Functor的定义
class Functor f where
fmap :: (a -> b) -> f a -> f b

某个类型要能进行映射操作(map over),就必须继承Functor基类,并实现其中的fmap函数。我们来看一下几种默认的Functor形态:

  1. 列表list,非常好理解,操作列表我们一般使用map函数,它其实就是fmap针对列表的一个具体实例,在list中它们是等价的。

    -- 作为functor 的定义:
    instance Functor [] where
    fmap = map -- 实例
    fmap (*2) [1,2,3]
    > [2,4,6]
  2. Maybe,它是haskell中使用很广泛的数据类型,它有 Just 值Nothing 两种情况,分别用于表示成功和失败的情况。

    -- Maybe 的 functor 定义:
    instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing = Nothing -- 实例
    fmap (*2) (Just 1)
    > Just 2 fmap (*2) (Nothing)
    > Nothing
  3. IO,输入与输出,比如读取键盘输入,打印字符串等

    -- IO 的 Functor 定义
    instance Functor IO where
    fmap f action = do
    result <- action
    return (f result) -- 实例
    fmap ("hello! "++) getLine
    jeff -- 输入名字,打印时添加“hello”
    > "hello! jeff"

Functor的 (->) r 形态

(->) r 其实表示的是函数结合,也就是等价于 (.)

-- 下面两个定义是等价的,也就是 (->) r 形式下的 Functor 其实等价于 结合律
instance Functor ((->) r) where
fmap f g = (\x -> f (g x)) instance Functor ((->) r) where
fmap = (.) -- 实例
fmap (*3) (+100) 1
> 303 (*3) . (+100) $ 1
> 303

functor law

如果某个类型遵守这两个定律,那么它与其他Functor对于映射方面就具有相同的性质。

  1. fmap id = id

    如果我们对 functor 做 map id,那得到的新的 functor 应该要跟原来的一样

    fmap id (Just 3)
    > Just 3
    id (Just 3)
    > Just 3
  2. fmap (f . g) = fmap f . fmap g 也就是 functor 是能应用于函数结合的。

Applicative Functor

  为什么需要 Applicative Functor,什么情况下使用它。从Functor定义我们知道,fmap函数只能映射单个盒子,但假设需要映射两个三个,甚至是更多的盒子呢?或者是要处理返回值是函数的盒子呢?而这就是 Applicative Functor 要处理的情况。

  Applicative Functor 可以看作是Functor的增加版,从定义可知,它主要包括pure 和 <*>两个函数。

-- Applicative Functor 定义
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
  • pure :: a -> f a 意思就是把普通值放到默认的context(语义)下。比如如果是list,那么它代表的就是[ ] ,如果是Maybe,那么它就是 Just 值 / Nothing

  • (<*>) 接受一个装有函数的 functor 跟另一个 functor, 非常类似于fmap,它就像加强版的 fmap。以applicative style 的方式来使用 applicative functors。像是 pure f <*> x <*> y <*> ... 这个函数可以吃任意多的参数。

    -- 与fmap类型的对比,可以看出函数 a -> b 被装进了盒子 f 中
    (<*>) :: f (a -> b) -> f a -> f b
    fmap :: (a -> b) -> f a -> f b -- <*> 是左结合的,因此以下两个表达式是相等的
    pure (+) <*> Just 3 <*> Just 5
    (pure (+) <*> Just 3) <*> Just 5。
  • (<$>) 是applicative functor 中另一个很常用的符号,它其实就是中缀版的fmap。因为结合fmap写applicative functor更加方便。

    (<$>) :: (Functor f) => (a -> b) -> f a -> f b
    f <$> x = fmap f x
    -- 用<*>实现相同的功能
    pure f <*> x = fmap f x

接着看一下几个默认的 applicative functor,继承Applicative,必须实现 pure 和 (<*>) 函数

  1. Maybe 类型

    -- Maybe 的 Applicative 定义:
    instance Applicative Maybe where
    pure = Just
    Nothing <*> _ = Nothing
    (Just f) <*> something = fmap f something -- 实例
    pure (+3) <*> Just 9
    > Just 12 pure (+) <*> Just 3 <*> Just 5
    > Just 8
  2. 列表list 也是 applicative functor,从定义可以看出使用list的Applicative style完全可以实现 list comprehension 的功能。所以 Applicative style 对于 list 而言是取代某些类型的 list comprehension 的好方式。

    -- list 的定义
    instance Applicative [] where
    pure x = [x]
    fs <*> xs = [f x | f <- fs, x <- xs] -- 实例
    [(+3),(*2)] <*> [1,2]
    > [4,5,2,4] --下面表达式具有相同的功能
    (*) <$> [2,5,10] <*> [8,10,11] -- Applicative style
    [ x * y | x <- [2,5,10], y <- [8,10,11]] -- list comprehension
    > [16,20,22,40,50,55,80,100,110]
  3. IO ,下面的IO的实例,可以把 getLine 看做是一个去真实世界中拿取字串的盒子, 而 applicative functor 表达式会创造一个比较大的盒子,这个大盒子会派两个盒子去终端拿取字串,并把结果串接起来放进自己的盒子中。

    --IO 的 Applicative instance
    instance Applicative IO where
    pure = return
    a <*> b = do
    f <- a
    x <- b
    return (f x) -- 实例 将输入的两个字符串合并
    (++) <$> getLine <*> getLine
    aa
    bb
    > "aabb"

Applicative Functor 的 (->) r 形态

(->) r 形态定义

instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
  • 用 pure 将一个值包成 applicative functor 的时候,他产生的结果永远都会是那个值
  • 将两个 applicative functor 喂给 <*> 可以产生一个新的 applicative functor

接着综合使用上面的知识,来看一下实际应用applicative的几种方式。相比起functor,applicative functor要更强大和灵活。

-- 左结合形式, 第一项必须为含有函数的functor,右边全部为functor
pure (\x y z -> x+ y +z) <*> Just 3 <*> Just 4 <*> Just 5
> Just 12
[(+3),(*2)] <*> [1,2]
> [4,5,2,4] -- fmap(<$>) 形式,第一项为普通函数,右边都为functor
(+) <$> Just 1 <*> Just 2
> Just 3
(\x y z -> x + y +z) <$> [1,2] <*> [2,3] <*> [4,5]
> [7,8,8,9,8,9,9,10] -- (<$>) (->) r 形式,全部为普通函数,用单个参数调用执行
(\x y z -> [x,y,z]) <$> (3+) <*> (*100) <*> (`div`2) $ 2
> [5,200,1]

Applicative Functor 辅助函数

  1. liftA2

    只是applicative的套用函数而已,当然还有3个参数的版本 liftA3,而 liftA 则等价于 fmap

    -- 与applicative 的等价形式
    liftA2 f a b = f <$> a <*> b -- 以下表达式功能一致
    liftA2 (:) (Just 3) (Just [4])
    (:) <$> Just 3 <*> Just [4]
    pure (:) <*> Just 3 <*> Just [4] > Just [3,4]
  2. sequenceA

      当套用在函数上时,sequenceA 接受装有一堆函数的list,并回传一个回传list的函数。当我们有一串函数,想要将相同输入都喂给它们并查看结果的时候,sequenceA非常好用。

      当使用在 I/O action 上的时候,sequenceAsequence 是等价的。他接受一串 I/O action 并回传一个 I/O action,这个 I/O action 会计算 list 中的每一个 I/O action,并把结果放在一个 list 中

    -- 以下是两种实现sequenceA功能一致的函数
    sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
    sequenceA = foldr (liftA2 (:)) (pure []) sequenceA [Just 3, Just 2, Just 1]
    > Just [3,2,1] -- 将list组合成所有可能的组合
    sequenceA [[1,2,3],[4,5,6]]
    > [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] sequenceA [(>4),(<10),odd] 7
    map (\f -> f 7) [(>4),(<10),odd]
    > [True,True,True] -- and接受一串Bool,并在所有值都为True时才返回True
    and $ sequenceA [(>4),(<10),odd] 7
    and $ map (\f -> f 7) [(>4),(<10),odd]
    > True

Haskell学习-functor的更多相关文章

  1. Haskell学习-monad

    原文地址:Haskell学习-monad 什么是Monad Haskell是一门纯函数式的语言,纯函数的优点是安全可靠.函数输出完全取决于输入,不存在任何隐式依赖,它的存在如同数学公式般完美无缺.可是 ...

  2. Haskell学习-函数式编程初探

    原文地址:Haskell学习-函数式编程初探   为什么要学习函数式编程?为什么要学习Haskell?   .net到前端,C#和JavaScript对我来说如果谈不上精通,最起码也算是到了非常熟悉的 ...

  3. Haskell学习-高阶函数

    原文地址:Haskell学习-高阶函数 高阶函数(higher-order function)就是指可以操作函数的函数,即函数可以作为参数,也可以作为返回结果.有了这两个特性,haskell可以实现许 ...

  4. haskell学习笔记_函数

    一开始学习函数式编程语言就被告知函数式编程语言是一种“定义式”的语言,而不是一种命令式的语言,在学习haskell的函数语法时,此感觉更加强烈,haskell的函数定义倾向于一种类似C++里面的swi ...

  5. haskell学习笔记<1>--基本语法

    七月记录:整个七月就在玩,参加夏令营,去遨游.... 八月份需要开始复习,正等书的这个过程突然想起一直没有完成的学习-haskell,所以当前的目标是用haskell制作一个局域网通信的小工具,要求: ...

  6. 「Haskell 学习」二 类型和函数(上)

    随着学习的深入,笔记会补充和修订.当然,这个补充修订也许会鸽,但我一定会坚持写完. 这个笔记假定你至少学过C/C++及Python,或与这两种语言类型相同的语言. 类型系统概述 “Haskell’s ...

  7. haskell学习资料

    Haskell基础语法 Real World Haskell 中文版 Haskell趣学指南

  8. 「Haskell 学习」一 环境与大致了解

    感谢<Real World Haskell>在网上的免费发布,可以白嫖学Haskell这个久闻大名的函数式编程语言了. 本文运行于openSUSE Tumbleweed下,运行相关命令时留 ...

  9. Haskell 学习

    truncate pi -- 表示截断, 此处结果为 3 haskell中的touple是可变的,而python中是不可变的 lines函数: lines :: String -> [Strin ...

随机推荐

  1. SharePoint2013 功能区的配置

    遇到了很多次对网站功能区个性化定义的任务,包括标签按钮之类的修改,每次都要重新翻书,这次总结一下,留作备用. 添加内容 下面的XML我认为主要的内容是四部分,一个是CommandUIDefinitio ...

  2. 一天搞懂深度学习-训练深度神经网络(DNN)的要点

    前言 这是<一天搞懂深度学习>的第二部分 一.选择合适的损失函数 典型的损失函数有平方误差损失函数和交叉熵损失函数. 交叉熵损失函数: 选择不同的损失函数会有不同的训练效果 二.mini- ...

  3. Spring JTA multiple resource transactions in Tomcat with Atomikos example

    http://www.byteslounge.com/tutorials/spring-jta-multiple-resource-transactions-in-tomcat-with-atomik ...

  4. 基于.net的爬虫应用-DotnetSpider

    最近应朋友的邀请,帮忙做了个简单的爬虫程序,要求不高,主要是方便对不同网站的爬取进行扩展,获取到想要的数据信息即可.当然,基于数据的后期分析功能是后话,以后的随笔我会逐步的介绍. 开源的爬虫框架比较多 ...

  5. PAT1129:Recommendation System

    1129. Recommendation System (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue ...

  6. EasyUI集成Kindeditor使用

    在实际的项目中,我们需要在项目中集成富文本编辑器,而kindeditor作为一款优良的编辑器,在项目中或多或少都会用到! 实际效果图     使用方法: 1.首先下载Kindeditor编辑器,我这里 ...

  7. SSM-SpringMVC-25:SpringMVC异常顶级之自定义异常解析器

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 上篇博客相信大家也看到了,自定义异常,用了SimpleMappingExceptionResolver这个解析 ...

  8. 远程备份binlog服务

    Ⅰ.bonlog server介绍 对于binlog的备份,之前文章里说的是有从机,一般不备份,那现在人家就是 要备份嘛,怎么办嘛, 写个脚本每天夜里去把前一天产生的binlog拷贝出来可以不? 行啊 ...

  9. Android 高仿微信6.0主界面 带你玩转切换图标变色

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41087219,本文出自:[张鸿洋的博客] 1.概述 学习Android少不了模仿 ...

  10. Python 的 urllib.parse 库解析 URL

      Python 中的 urllib.parse 模块提供了很多解析和组建 URL 的函数. 解析url urlparse() 函数可以将 URL 解析成 ParseResult 对象.对象中包含了六 ...