这话题总长谈了,在面试中必定考核的能力受到,我个人认为解决问题能力是割除第一个之,比学习能力优先级重胜似。解决问题之力量既能看到程序员的思维能力,应变能力,探索能力相当于,又好视他的阅历。如果解决问题能力不完美是心有余而力不足透过面试的。

   
在高达平等节咱们谈谈了Monoid的结合性和固定等值的意图和Monoid如何与串类元素折叠算法相配合。不过我们仅示范了转基础项目(primitive
type)Monoid实例的施用,所以上一致节省之座谈目的是理论多被履行。在这同省咱们将将关键放在有实用综合型(composite
type)Monoid实例及Monoid的悬空表达和函数组合能力。

这边选出个例证,假如自己执行了一个PHP的本子,如php
test.php,预期是足以回来一个字符串。但履后并未任何信息输出,这时候通过什么措施能够领略程序错在乌?这里可以以缓解问题能力分为8只级次,越到后面的象征能力尤其强。

   
Monoid的次最先操作函数有整合特性(associativity),与稳定等值(identity)共同采取可以任意采用左折叠或右折叠算法处理串类元素(List
element)而获取同结果。所以使用Monoid
op我们可以得出左折叠等于右折叠的定论:

Lv0 查看PHP错误信息

先后尚未直达预期效益,证明代码出错了,看PHP的错误信息是率先步。如果一直忽略错误信息,表明是人口无适合当专业的程序员岗位。有些情况下php.ini配置中关闭了错显示,需要改php.ini打开错误信息,或者错误信息被传出至了日记文件,这种状态好直接tailf
php_error.log来拘禁错误信息。

用到错误信息后直接固定到程序代码问题,或者到Google/百度搜索,即可缓解问题。

流动:打开左显示的点子是

  • php.ini中display_errors / display_startup_errors 设置为On
  • php.ini中error_reporting 设置为E_ALL
  • PHP代码中装置error_reporting(E_ALL)

左折叠:op(op(op(a,b),c),d)

Lv1 在多个版本的php或php-cli与php-fpm加载不同的配置

存在多独版本的php,懂得通过which
php来拘禁是何人PHP,或者加绝对路径制定php版本。表示这PHPer通过了此层级的50%考验。

另外一个情形就是是php-cli与php-fpm得到的履行情况不均等,如以web浏览器中实行是针对性的,cli下执行是拂的。这时候可能是2独环境加载的php.ini不同所给予。cli下经php
-i |grep
php.ini得到加载了哪个php.ini。而fpm下通过phpinfo()函数可以拿走php.ini的绝对路径。

 

右折叠:op(a,op(b,op(c,d)))

Lv2 var_dump/die打印变量值信息单步调试

随即是惯用的程序调试手段,也是最最简便粗暴有效的缓解问题方法。高级一点的一手是采用PHP的Trace类/日志类,花哨一点之得凭借phpstorm+xdebug在IDE工具里展开Debug。

Trace工具还好分析脚本的耗时,进行PHP程序的特性优化。

即3独考验全部透过,表明此程序员已经具备了正规PHP程序员应该有些解决问题能力了。PHP程序员只要过了这阶段,就足以应多异常一部分情况,在中小型网站中永不压力。

 

但,如果会用并行算法的言辞就是是:

Lv3 应用strace工具跟踪程序执行

strace可以为此来查系统调用的实施,使用strace php test.php,或者strace -p
进程ID。strace就足以协助您通过现象看本质,掌握程序执行的历程。这个手法是以巨型网站,大柜里最为常用的。如果没有控制strace,这里不得不说对不起了,我们无接受不见面strace的PHPer。

strace其实也是针对性程序员基础之考验,如果未懂得操作操作系统,完全不知道底层,肯定吗达成不顶会用strace的水平。当然strace对于PHP代码里的死循环是解决不了的。比如您发觉一个php-fpm进程CPU100%了,strace恐怕是解决不了的。因为strace是圈系统调用,一般还是IO类操作,既然是IO密集,那CPU一定非可能是100%。

 

并行算法:op(op(a,b),op(c,d))
我们好又运算 op(a,b), op(c,d)

Lv4 使用tcpdump工具分析网络通信过程

tcpdump可以抓到网卡的数额通信过程,甚至数内容吗可抓到。使用tcpdump可以看到网通信过程是如何的,如何时发起了TCP
SYN3次于握手,何时发送FIN包,何时发送RST包。这是一个基础,如果无知情tcpdump,证明不抱有网络问题化解能力。

 

比方我们得以运用并行算法的语,肯定会升级计算效率。试想倘若我们针对一个重特大文件进行平和字数统计或者找最好酷价值什么的,我们可以把此可怜文件分为多多少文件然后又计算后再行商议将节约成千上万测算时。在现世坏数据时代,数据文件不但非常并且是分布于众服务器上之。那么Monoid的奇定律就足以使数码处理相互运算,特别配合大数量处理模式。

Lv5 统计函数调用的耗时和成功率

动xhporf/xdebug导出PHP请求的调用过程,然后分析每个函数调用的历程与耗时。能够分析PHP程序的性能瓶颈,找有好优化的触发。

除此以外一个于网络服务的调用,如mysql查询,curl,其他API调用等,通过记录起始与终止时microtime,返回的是匪是false,可以取调用是否成,耗时多少。如果得以集中数据,整理起调用的成功率,失败率,平均延时,证明此程序员对接口质量敏感,有大型网站项目更。

 

咱们看看能不能够实现像折叠算法似的并行算法:

Lv6 gdb使用

gdb是C/C++调试程序的利器,需要具备一定C/C++功底的程序员才见面会运用自如运用gdb。上面说之strace无法跟踪php程序CPU100%,而gdb是可跟的。另外gdb也得以化解php程序core
dump的问题。

通过gdb -p 进程ID,再配合php-src的.gdbinit
zbacktrace等工具,可以充分便利地跟PHP程序的履行。像面的CPU100%频繁是PHP程序中生死循环了,gdb进行反复查,就大概可博得死循环的位置。具备gdb解决问题能力的PHP程序员少之又少。如果能以gdb解决PHP问题,这个PHPer百分之百可以透过面试,并且可用到比较高的技术评级。

 

1   def foldMapV[A,B](iseq: IndexedSeq[A])(m: Monoid[B])(f: A => B): B = {
2       if (iseq.isEmpty) m.zero
3       else if (iseq.length == 1) f(iseq(0))
4       else {
5           val (l, r) = iseq.splitAt(iseq.length / 2)
6           m.op(foldMapV(l)(m)(f), foldMapV(r)(m)(f))
7       }
8   }                                               //> foldMapV: [A, B](iseq: IndexedSeq[A])(m: ch10.ex2.Monoid[B])(f: A => B)B

Lv7 查看PHP内核和壮大源码

而会熟悉PHP内核和扩张的源码,遇到PHP程序中极其复杂的内存错误,也堪有缓解之力。这类PHP程序员就是凤毛麟角了。配合gdb工具和针对性PHP源码的耳熟能详,可以翻opcode的消息,execute_data的内存,全局变量的状态等。

中转大神博文,地址:http://rango.swoole.com/archives/340 

哟,这个foldMapV从表面,在项目款式上同foldMap相同,不同的凡内部具体落实方式;foldMapV循环把目标串进行分半。

重组前当并行运算的座谈,我们所以以下措施应该可以兑现相互之间运算吧:

1   def foldMapV[A,B](iseq: IndexedSeq[A])(m: Monoid[B])(f: A => B): B = {
2       if (iseq.isEmpty) m.zero
3       else if (iseq.length == 1) f(iseq(0))
4       else {
5           val (l, r) = iseq.splitAt(iseq.length / 2)
6           m.op(async(foldMapV(l)(m)(f)), async(foldMapV(r)(m)(f)))
7       }
8   }                                               //> foldMapV: [A, B](iseq: IndexedSeq[A])(m: ch10.ex2.Monoid[B])(f: A => B)B

好,我们下面找个例来演示高阶类型Monoid实例和互运算应用:用Monoid来实现对字串(List[String])的和平字数统计。由于我们打算动用并行计算,对许串进行分半时会容许会见起雷同配分成两半之图景,所以要打定义复杂一点底数据类型:

 1   trait WC 
 2   case class Stub(chars: String) extends WC  //chars 记录了未完整文字的字符
 3   case class Part(lStub: String, words: Int, rStub: String) extends WC   //lStub=左边文字结尾, words=完整字数,rStub=右边文字开头
 4   
 5   def wcMonoid: Monoid[WC] = new Monoid[WC] {
 6       def op(wc1: WC, wc2: WC): WC = {
 7           (wc1, wc2) match {
 8               case (Stub(l),Stub(r)) => Stub(l + r)
 9               case (Stub(lb),Part(le,w,rb)) => Part(lb+le,w,rb)
10               case (Part(le,w,rb),Stub(re)) => Part(le,w,rb+re)
11               case (Part(le,w,rb),Part(lb,w2,re)) => Part(le, w + (if((rb+lb).isEmpty) 0 else 1) + w2, re)
12           }
13       }
14       val zero = Stub("")
15   }

 Monoid[WC]大凡个WC类型的Monoid实例,op(wc1,wc2)=wc3虽然把个别只WC值拼凑起来变成任何一个WC值。下面为咱们用wcMonoid来贯彻此字统计函数:

 1   def wordCount(ws: String): Int = {
 2     def wc(c: Char): WC = {
 3         if (c.isWhitespace) Part("",0,"")
 4         else Stub(c.toString)
 5     }
 6     def unstub(s: String) = s.length min 1
 7     
 8     foldMapV(ws.toIndexedSeq)(wcMonoid)(wc) match {
 9         case Stub(c) => 0
10         case Part(l,w,r) => unstub(l) + w + unstub(r)
11     }
12   }                                               //> wordCount: (ws: String)Int

 

所以它来反复只字数:

1   val words = "the brown fox     is running quickly."  //故意留点空格,标点符号什么的
2                                                   //> words  : String = the brown fox     is running quickly.
3   wordCount(words)                                //> res0: Int = 6 

没错!

再来一个例子:检查一错数字是否是一动不动的:

 1   def seqIsOrdered(iseq: IndexedSeq[Int]): Boolean = {
 2       val stateMonoid = new Monoid[Option[(Int, Int, Boolean)]] {    //状态:Option[(最小值,最大值,是排序)]
 3          def op(s1: Option[(Int,Int,Boolean)], s2: Option[(Int,Int,Boolean)]): Option[(Int,Int,Boolean)] = {
 4            (s1,s2) match {
 5               case (None, b) => b
 6               case (b, None) => b
 7               case (Some((x1,y1,b1)),Some((x2,y2,b2))) => Some(x1 min x2,y1 max y2, b1 && b2 && x2 >= y1)
 8            }
 9          }
10          val zero = None
11       }
12       foldMapV(iseq)(stateMonoid)(i => Some(i,i,true)) map (_._3) getOrElse true
13   }

1   seqIsOrdered(List(1,2,5,9,33).toIndexedSeq)     //> res0: Boolean = true
2   seqIsOrdered(List(1,2,5,0    ,33).toIndexedSeq)    //> res1: Boolean = false

当此事例里我们因而Option[min,max,ordered]作为当前状态并用stateMonoid来处理此状态。foldMapV参数(i
=> Some(i,i,true))就是规范的 A =>
B。还记也,我们增加foldMap这个函数是的目的是一旦元素A没有Monoid实例,那么我们得为此Monoid[B]下一场据此A
=>B函数把A转成为B才能够采取Monoid[B]。这里我们管
i转成Some(Int,Int,Boolean)。

值得注意的是上述两个例证foldMapV历遍无论如何是不见面中途退出的。这个特性把foldMapV的利用局限在必得吃整个数据源的测算以,如要与、最大值等等。对于另外一些求,如:A
=>
Boolean这种要求,即使第一独价就已经落答案也亟须走完整串数据。

咱俩于头里的章里已讨论了部分数据结构如List,Stream,Tree等。当我们得处理这些构造中封装的因素时便采取一些算法如沁算法。这种算法能保存数据结构。而且它们有共通性:都足以以折叠算法。既然有共性,肯定就是见面起深抽象的半空中,我们可管其抽象表达成一个Foldable[F[_]]:List,Stream,Tree等数据结构类型就是F[_];一个数据结构中查封装了部分因素。这个Foldable类型可以涵盖各种历遍算法:

 1  def endoComposeMonoid[A] = new Monoid[A => A] {
 2       def op(f: A => A, g: A => A): A => A = f compose g
 3       val zero = (a: A) => a
 4   }
 5   def dual[A](m: Monoid[A]): Monoid[A] = new Monoid[A] {
 6       def op(a1: A, a2: A) = m.op(a2,a1)
 7       val zero = m.zero
 8   }
 9   trait Foldable[F[_]] {
10      def foldRight[A, B](as: F[A])(z: B)(f: (A, B) => B): B = {
11            foldMap(as)(endoComposeMonoid[B])(a => b => f(a,b))(z)
12      }
13      def foldLeft[A, B](as: F[A])(z: B)(f: (B, A) => B): B = {
14            foldMap(as)(dual(endoComposeMonoid[B]))(a => b => f(b,a))(z)
15      }
16      def foldMap[A, B](as: F[A])(mb: Monoid[B])(f: A => B): B = {
17            foldLeft(as)(mb.zero)((b,a) => mb.op(f(a),b))
18      }
19      def concatenate[A](as: F[A])(m: Monoid[A]): A = {
20         foldLeft(as)(m.zero)(m.op)
21      }
22   }

俺们今天早就获取了个Foldable抽象数据结构,它含有了多折叠历遍算法。我们可以试着创造有Foldable实例看看:

 1   object listFoldable extends Foldable[List] {
 2      override def foldRight[A, B](as: List[A])(z: B)(f: (A, B) => B): B = {
 3            as.foldRight(z)(f)
 4      }
 5      override def foldLeft[A, B](as: List[A])(z: B)(f: (B, A) => B): B = {
 6            as.foldLeft(z)(f)
 7      }
 8      override def foldMap[A, B](as: List[A])(mb: Monoid[B])(f: A => B): B = {
 9            as.foldLeft(mb.zero)((b,a) => mb.op(f(a),b))
10      }
11      override def concatenate[A](as: List[A])(m: Monoid[A]): A = {
12         as.foldLeft(m.zero)(m.op)
13      }
14   }
15   object indexedSeqFoldable extends Foldable[IndexedSeq] {
16      override def foldRight[A, B](as: IndexedSeq[A])(z: B)(f: (A, B) => B): B = {
17            as.foldRight(z)(f)
18      }
19      override def foldLeft[A, B](as: IndexedSeq[A])(z: B)(f: (B, A) => B): B = {
20            as.foldLeft(z)(f)
21      }
22      override def foldMap[A, B](as: IndexedSeq[A])(mb: Monoid[B])(f: A => B): B = {
23            as.foldLeft(mb.zero)((b,a) => mb.op(f(a),b))
24      }
25      override def concatenate[A](as: IndexedSeq[A])(m: Monoid[A]): A = {
26         as.foldLeft(m.zero)(m.op)
27      }
28   }
29   object StreamFoldable extends Foldable[Stream] {
30      override def foldRight[A, B](as: Stream[A])(z: B)(f: (A, B) => B): B = {
31            as.foldRight(z)(f)
32      }
33      override def foldLeft[A, B](as: Stream[A])(z: B)(f: (B, A) => B): B = {
34            as.foldLeft(z)(f)
35      }
36   }

再看看Tree foldable
实例:

 1  trait Tree[+A] 
 2   case class Leaf[A](value: A) extends Tree[A]
 3   case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
 4   object TreeFoldable extends Foldable[Tree] {
 5       override def foldMap[A, B](as: Tree[A])(mb: Monoid[B])(f: A => B): B = {
 6           as match {
 7               case Leaf(a) => f(a)
 8               case Branch(l,r) => mb.op(foldMap(l)(mb)(f),foldMap(r)(mb)(f))
 9           }
10       }
11       override def foldRight[A, B](as: Tree[A])(z: B)(f: (A, B) => B): B = {
12           as match {
13               case Leaf(a) => f(a,z)     //恒等值定律 
14               case Branch(l,r) => foldRight(l)(foldRight(r)(z)(f))(f)
15           }
16       }
17       override def foldLeft[A, B](as: Tree[A])(z: B)(f: (B,A) => B): B = {
18           as match {
19               case Leaf(a) => f(z,b)
20               case Branch(l,r) => foldLeft(r)(foldLeft(l)(z)(f))(f)
21           }
22       }
23   }

足见见TreeFoldable的兑现方式与List,Stream等串类数据类型有所不同。这是为Tree类型没有成的叠算法。再不怕Tree类型没有空值(只有Leaf,
Branch)。这个特点暗示着来来项目的Monoid是从未永恒等值的。我们统称这些品种为semigroup。

 Option的foldable与TreeFoldable很像:

 1   object OptionFoldable extends Foldable[Option] {
 2        override def foldMap[A, B](opt: Option[A])(mb: Monoid[B])(f: A => B): B = {
 3            opt match {
 4                case None => mb.zero
 5                case Some(a) => f(a)
 6            }
 7        }
 8     override def foldRight[A, B](opt: Option[A])(z: B)(f: (A, B) => B): B = {
 9      opt match {
10          case None => z
11          case Some(a) => f(a,z)
12      }
13     }
14     override def foldLeft[A, B](opt: Option[A])(z: B)(f: (B,A) => B): B = {
15         opt match {
16             case None => z
17             case Some(a) => f(z,a)
18         }
19     }
20   }

 

实则任何可折的数据类型都可于转移成为List类型,因为我们可以用折叠算法重组List:

 1  trait Foldable[F[_]] {
 2      def foldRight[A, B](as: F[A])(z: B)(f: (A, B) => B): B = {
 3            foldMap(as)(endoComposeMonoid[B])(a => b => f(a,b))(z)
 4      }
 5      def foldLeft[A, B](as: F[A])(z: B)(f: (B, A) => B): B = {
 6            foldMap(as)(dual(endoComposeMonoid[B]))(a => b => f(b,a))(z)
 7      }
 8      def foldMap[A, B](as: F[A])(mb: Monoid[B])(f: A => B): B = {
 9            foldLeft(as)(mb.zero)((b,a) => mb.op(f(a),b))
10      }
11      def concatenate[A](as: F[A])(m: Monoid[A]): A = {
12         foldLeft(as)(m.zero)(m.op)
13      }
14      def toList[A](as: F[A]): List[A] = {
15          foldRight(as)(Nil: List[A]){_ :: _}
16      }
17   }

Monoid的函数组合能力为生有趣:如果我们出Monoid[A],
Monoid[B],那我们就算可以将她做成Monoid[(A,B)]:

1   def productMonoid[A,B](ma: Monoid[A], mb: Monoid[B]): Monoid[(A,B)] = new Monoid[(A,B)] {
2       def op(x: (A,B), y: (A,B)) = (ma.op(x._1, y._1), mb.op(x._2,y._2))
3       val zero = (ma.zero, mb.zero)
4   }                                               //> productMonoid: [A, B](ma: ch10.ex2.Monoid[A], mb: ch10.ex2.Monoid[B])ch10.e
5                                                   //| x2.Monoid[(A, B)]

 我们得就此这做的Monoid在历遍一个List时又计算List长度及同:

1  val pm = productMonoid(intAdditionMonoid,intAdditionMonoid)
2                                                   //> pm  : ch10.ex2.Monoid[(Int, Int)] = ch10.ex2$$anonfun$main$1$$anon$6@614c55
3                                                   //| 15
4   listFoldable.foldMap(List(1,2,3,4))(pm)(a => (1, a))
5                                                   //> res0: (Int, Int) = (4,10)

当历遍过程中我们将List每个节点初次素值转成为一对值
a => (1, a),然后分别针对每个成员执行intAdditionMonoid的op操作。

脚剩下的工夫我们再次议论一些于复杂的Monoid:

若一个函数的结果是Monoid,我们得实现此函数的Monoid实例:

1   def functionMonoid[A,B](mb: Monoid[B]): Monoid[A => B] = new Monoid[A => B] {
2       def op(f: A => B, g: A => B): A => B = a => mb.op(f(a),g(a))
3       val zero: A => B = a => mb.zero 
4   }

心想事成者Monoid实例的该尽可能从种类匹配入手:函数A
=> B的结果是B;我们来Monoid[B],Monoid[B].op(b,b)=>b。

再来一个合并key-value
Map的Monoid实例:如果我们发value类型的Monoid实例就可以实现:

1   def mapMergeMonoid[K,V](mv: Monoid[V]): Monoid[Map[K,V]] = new Monoid[Map[K,V]] {
2        val zero = Map()
3       def op(ma: Map[K,V], mb: Map[K,V]): Map[K,V] = {
4           ma.map {
5               case (k,v) => (k, mv.op(v, mb get(k) getOrElse mv.zero ))
6           }
7       }
8   }

出矣这Monoid实例,我们不怕得拍卖Map的嵌入表达式了:

 1   val M: Monoid[Map[String, Map[String, Int]]] = mapMergeMonoid(mapMergeMonoid(intAdditionMonoid))
 2   M: Monoid[Map[String, Map[String, Int]]] = $anon$1@21dfac82
 3   
 4  val m1 = Map("o1" -> Map("i1" -> 1, "i2" -> 2))
 5  m1: Map[String,Map[String,Int]] = Map(o1 -> Map(i1 -> 1, i2 -> 2))
 6  
 7  val m2 = Map("o1" -> Map("i2" -> 3))
 8  m2: Map[String,Map[String,Int]] = Map(o1 -> Map(i2 -> 3))
 9  
10  val m3 = M.op(m1, m2)
11  m3: Map[String,Map[String,Int]] = Map(o1 -> Map(i1 -> 1, i2 -> 5))

末段,我们尝试着用mapMergeMonoid实例来贯彻frequencyMap:计算输入List里之文发现次数。如果就此一个例子来说明的话,看看下面这一串文字转成为key-value
Map:

Vector(“a rose”, “is
a”, “rose is”, “a rose”)
>>> Map(a -> 3, rose -> 3, is -> 2)

马上不纵是找引擎中的目录比重算法为?我们因此foldMapV和mapMergeMonoid可以相互运算整理索引,这到底Monoid的莫过于运用之一。

俺们看实际实现:

1 def frequencyMap[A](as: IndexedSeq[A]): Map[A, Int] =
2   foldMapV(as, mapMergeMonoid[A, Int](intAddition))((a: A) => Map(a -> 1))

1 frequencyMap(Vector("a rose", "is a", "rose is", "a rose"))
2 res0: Map[String,Int] = Map(a -> 3, rose -> 3, is -> 2)