【数学模型】商人们怎样过河?

这次的问题是一个很经常遇到的过河问题,其实对于该类问题,我们经过逻辑思考就可以得到答案。但是通过数学模型的建立,我们可以得到一个通用的解答,并且通过计算机的计算我们可以大大扩大问题的规模。

问题分析

因为这个问题已经理想化了,所以我们无需对模型进行假设,该问题可以看作一个多步决策问题

每一步,船由此岸划到彼岸或者由彼岸划回此岸,都要对船上的人员进行决策(此次渡河船上可以有几名商人和几名随从),在保证安全(两岸的随从都不比商人多)的前提下,在有限次的决策中使得所有人都到对岸去。

因此,我们要做的就是要确定每一步的决策,达到渡河的目标。

建立模型

记第 k 次过河前此岸的商人数为 xk , 随从数为 yk , k = 1, 2, 3…, xk ,yk = 0, 1, 2, 3

定义状态: 将二维向量 sk = ( xk , yk ) 定义为状态

将安全渡河状态下的状态集合定义为允许状态集合, 记为

S = {(x,y) | x=0,y=0,1,2,3; x=y=1; x=y=2; x=3,y=0,1,2,3}

记第 k 次渡河船上的商人数为 uk , 随从数为 vk

定义决策: 将二维向量 dk = (uk , vk) 定义为决策

允许决策集合 记作

D = {(u,v) | 1 ≤ u+v ≤ 2, u,v = 0,1,2}

因为小船容量为2,所以船上人员不能超过2,而且至少要有一个人划船,由此得到上式。

由我们定义的状态 sk 和决策 dk ,我们可以发现它们之间是存在联系的:

  • k 为奇数是表示船由此岸划向彼岸,k 为偶数时表示船由彼岸划回此岸
  • 状态 sk 是随着决策 dk 变化的,规律为:
sk+1 = sk + (-1)kdk

我们把上式称为状态转移律,因此渡河方案可以抽象为如下的多步决策模型

求决策 dk ∈ D(k = 1,2,…,n) , 使状态 sk ∈ S 按照转移率,初始状态 s1 = (3,3) 经有限步 n 到达状态 sn+1 = (0,0)

到这里,整个数学模型就已经非常清晰了,接下来要做的就是求解模型得出结果。

求解模型

在这个模型的求解中,我将会使用两种方法,一种是数学图解法,用于解决和当前题目一样的规模比较小的问题,优点是比较简便,但是对于规模比较大的问题就无能为力了,比如说有50个商人携带50个随从过河,第二种方法是通过计算机编程,使用程序来解决该问题,即使问题规模增大,我们也可以利用计算机强大的计算能力来解决。

数学图解法

我们首先在 xOy 平面坐标系中画出如下方格,方格中的点表示状态 s = (x,y)

image

起始状态(下图绿色点) s1 = (3,3) , 终止状态(下图红色点) sn+1 = (0,0)

image

允许决策 dk 表示的是在方格中的移动,根据允许决策 dk 的定义,它每次的移动范围为1~2格,并且 k 为奇数时向左或下方或左下方移动,k 位偶数时向右或上方或右上方移动。

于是,这个问题就变成了,根据允许决策 dk ,在方格中在状态(方格点)之间移动,找到一条路径,使得能从起始状态(上图绿色点) s1 = (3,3) ,到达终止状态(上图图红色点) sn+1 = (0,0)

在下图中,我们给出了一种方案,我们可以很清楚的看到该方案绝对不是最佳方案(渡河次数最少),它只是给出了一种方案,而且我们看来是一种极其不优化的方案,但是可以很清楚地看出图解法是如何工作的。

image

根据上图,我们得出的方案如下:

  • d1:两个随从划到对岸
  • d2:一个随从划回来
  • d3:两个随从划到对岸
  • d4:一个随从划回来
  • d5:两个商人划到对岸
  • d6:一个商人和一个随从划回来
  • d7:两个商人划到对岸
  • d8:一个随从划回来
  • d9:两个随从划到对岸
  • d10:一个商人划回来
  • d11:一个商人和随从划到对岸

最终商人们安全渡河

程序求解

我们看到上面介绍的图解法对于小规模问题很直观也很简单,但是无法应对大规模的问题,于是我们采用编程的方法来再次解决上述问题,这次我使用的编程语言为Python.

创建允许状态集合

对于允许状态集合,我们要去使用算法对其进行计算,所谓允许状态无非就是,河岸两边的商人们都是安全的:

  • 一种情况是:两岸的商人人数都比随从人数多(对于随从和商人人数相同的情况就是河的任一岸,商人人数等于随从人数)
  • 另一情况为:所有商人都在河的任何一岸,此时另一岸没有任何商人,对于随从的人数在河的任一岸的数量不论是多少,此时都是安全的

按照以上方法编程如下:

1
2
3
4
5
6
7
8
9
10
11
12
'''创建允许状态集合'''    
def allowset(self):
    allowset = []
    for i in range(self.merchants + 1):
        for j in range(self.servants + 1):
            if i == 0:
                allowset.append([i,j])
            elif i == self.merchants:
                allowset.append([i,j])
            elif (i >= j and ((self.merchants-i) >= (self.servants-j))):
                allowset.append([i,j])
    return allowset

创建允许决策集合

对于创建允许决策集合,它和船的容量是相关的,只要每次渡河的商人数量和随从数量小于等于船的容量即可,代码如下:

1
2
3
4
5
6
7
8
'''创建允许决策集合'''
def allowaction(self):
    allowactionset = []
    for i in range(self.capacity + 1):
        for j in range(self.capacity + 1):
            if (i+j) <= self.capacity and (i + j) != 0:
                allowactionset.append([i,j])
    return allowactionset

如何渡河

对于如何渡河问题我采取的是一种随机的方法,对于当前安全状态,随机选择一种决策进行试探,如果采取该决策可以到达安全状态,则采用,如此循环,直到到达目的地。如果采取该策略不能到达安全状态,则再次随机选择一种策略。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
def solve(self,allowactionset,allowstate):
    count = 1;
    current = (self.merchants,self.servants)
    while current != [0,0]:
        move = allowactionset[random.randint(0,len(allowactionset)-1)]
        temp = [current[0]+((-1)**count)*move[0],current[1]+((-1)**count)*move[1]]
        if(temp in allowstate):
            current = [current[0]+((-1)**count)*move[0],current[1]+((-1)**count)*move[1]]
            if(count % 2 == 1):
                print "[%d]个商人,[%d] 个随从从此岸划到对岸" %(move[0],move[1])
            elif(count % 2 == 0):
                print "[%d]个商人,[%d] 个随从从对岸划回此岸" %(move[0],move[1]) 
        count = count + 1

完整代码

有了以上算法之后,我们就可以使用计算机来解决一些较大规模的问题了,完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# -*- coding: utf-8 -*-
# Copyright (c) 2015 Jason Luo @ SDU

"""解决商人安全过河问题"""

import random

class Boat(object):
    def __init__(self, merchants, servants, capacity):
        self.merchants = merchants
        self.servants = servants
        self.capacity = capacity
        
        print "Initialize: [%d] merchants and [%d] servants" %(merchants, servants)
        
    '''创建允许状态集合'''    
    def allowset(self):
        allowset = []
        for i in range(self.merchants + 1):
            for j in range(self.servants + 1):
                if i == 0:
                    allowset.append([i,j])
                elif i == self.merchants:
                    allowset.append([i,j])
                elif (i >= j and ((self.merchants-i) >= (self.servants-j))):
                    allowset.append([i,j])
        return allowset

    '''创建允许决策集合'''
    def allowaction(self):
        allowactionset = []
        for i in range(self.capacity + 1):
            for j in range(self.capacity + 1):
                if (i+j) <= self.capacity and (i + j) != 0:
                    allowactionset.append([i,j])
        return allowactionset

    '''渡河'''
    def solve(self,allowactionset,allowstate):
        count = 1;
        current = (self.merchants,self.servants)
        while current != [0,0]:
            move = allowactionset[random.randint(0,len(allowactionset)-1)]
            temp = [current[0]+((-1)**count)*move[0],current[1]+((-1)**count)*move[1]]
            if(temp in allowstate):
                current = [current[0]+((-1)**count)*move[0],current[1]+((-1)**count)*move[1]]
                if(count % 2 == 1):
                    print "[%d]个商人,[%d] 个随从从此岸划到对岸" %(move[0],move[1])
                elif(count % 2 == 0):
                    print "[%d]个商人,[%d] 个随从从对岸划回此岸" %(move[0],move[1])
 
            count = count + 1


'''主方法'''            
def main():
    boat = Boat(3,3,2)
    allowstate = boat.allowset()
    print "允许状态集合为:"
    print allowstate

    actionset = boat.allowaction()
    print "允许决策集合为:"
    print actionset

    boat.solve(actionset,allowstate)


if __name__ == '__main__':
    main()

运行结果

为了缩短运行结果的篇幅,我们同样采取小规模问题来验证算法的正确性,这里还是采用原问题规模,运行结果如下:

1
Initialize: [3] merchants and [3] servants
允许状态集合为:
[[0, 0], [0, 1], [0, 2], [0, 3], [1, 1], [2, 2], [3, 0], [3, 1], [3, 2], [3, 3]]
允许决策集合为:
[[0, 1], [0, 2], [1, 0], [1, 1], [2, 0]]
[1]个商人,[1] 个随从从此岸划到对岸
[2]个商人,[0] 个随从从此岸划到对岸
[2]个商人,[0] 个随从从对岸划回此岸
[2]个商人,[0] 个随从从此岸划到对岸
[0]个商人,[2] 个随从从此岸划到对岸

算法评价

该算法可以解决问题,但是有很多的不足,首先,由此算法得到的结果是随机的,它只是一个可行解,并不是最优解,并且其中很可能存在重复的步骤,对于一些超大规模的问题,它会产生许多重复的计算,其中会存在许多重复与环,还有许多可以改进的方法。这里我提出一种改进方法的思路,留待思考:我们可以借助图论中的深度优先算法来改进该问题从而得到最优解。如果不一定需要最优解的话,我们还可以在该算法上应用一个队列的数据结构,记录曾经在当前状态采取的策略,从而避免采取重复决策。

参考资料

ReactNative的架构设计

请注意,本篇写的是react native的架构设计,如果你用react来开发web程序,本篇文章只能仅供参考,问题都没有在web上去考虑过。

本篇较长,前面是目前flux开源框架的一些分析,后面是架构设计过程。您可以直奔主题。

用RN最大的难题是设计思想的转变,以前的设计方法论已经不太适用了。而RN仅仅提供了view的框架,构建完整app的架构并没有直接提供。

考虑目前遇到的如下问题,希望架构给出解决方案。

  1. 交互:如何解决组件间通信【父子、子父、兄弟等,特别是跨层or反向数据流动等】;用state还是接口操作组件;
  2. 职责:组件状态放哪,业务逻辑放哪,数据放哪,因为太灵活了,怎么做都可以实现功能,但是怎么做才是最好的,才是最正确的呢?

todo一个问题:由于react是面向状态编程,相当于react的组件只关注数据的最终状态,数据是怎么产生的并不关心,但是某些场景下,数据如何产 生的是会影响到组件的一些行为的【比如一个新增行要求有动画效果,查询出的行就不需要等】,这在RN中很难描述。。。。。

RN架构就是为解决上述问题提供的指导和方法论,是通盘考虑整个开发、测试、运维的状况,做出的考虑最全面的抉择,或者为抉择提供依据。

目前为react服务的架构也有一些了,如Flux,Reflux,Redux,Relay,Marty。

Flux

flux是官方提供的架构,目的是分层解耦,职责划分清晰,谁负责干啥很明确。具体描述可以参考官方文档,这里不详述。

  1. action 封装请求
  2. dispatcher 注册处理器、分发请求
  3. store 是处理器,处理业务逻辑,保存数据
  4. view 根据store提供的数据进行展现;接受用户的输入并发出action请求。

数据流动: Action-> Dispatcher -> Store -> Component

但我觉得解耦的太细了,干一个事,要做太多太多的额外工作了。

光注册监听动作就2次,一次是store注册到dispatcher,一次是view注册到store中。

而且,注册到dispatcher的监听应该都不叫注册,架构完全没有提供任何封装,直接暴露一个统一的回调方法,里面自行if else路由不同的store。

Reflux

结构上与flux架构基本一致,去掉了flux的一些冗余操作【比如没有了dispatcher】,架构更加简洁和紧凑,用到了一些约定大于配置的理念。

基本上将flux的架构冗余都简化了,可以说是flux的去冗余提升版,但是没有本质的变化。

╔═════════╗       ╔════════╗       ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝       ╚════════╝       ╚═════════════════╝
     ^                                      │
     └──────────────────────────────────────┘

  1. 更容易的监听。listenables和约定以on开头的方法。等。
  2. 去掉了dispatcher。
  3. action可以进行aop编程。
  4. 去掉了waitfor。store可以监听store。
  5. component提供了一系列mixin,方便注册\卸载到store的监听和与store交互等。

Redux

社区内比较受推崇,因为用起来相对比较简单

特性:

  1. 分层设计,职责清晰。
  2. 要求store reducer都是页面单例,易于管理。
  3. action为请求dto对象,是请求类型,请求数据的载体。
  4. reducer是处理请求的方法。不允许有状态,必须是纯方法。必须严格遵守输入输出,中间不允许有异步调用。不允许对state直接进行修改,要想修改必须返回新对象。
  5. store
    1. 维持应用的state;
    2. 提供 getState() 方法获取 state;
    3. 提供 dispatch(action) 方法分发请求来更新 state;门面模式,要求所有的请求满足统一的格式【可以进行路由、监控、日志等】,统一的调用方式。
    4. 通过 subscribe(listener) 注册监听器监听state的变化。
  6. 官方文档写的较为详细,从设计到开发都有,比flux要好

痛处如下,看能否接受或者解决:

  1. redux的原则1:state不能被修改。
    1. 其实这个用react的state也会有同样的问题,最好把state设计的没有冗余,尽量少出这种情况
    2. 解决方案:参考官方:因为我们不能直接修改却要更新数组中指定的一项数据,这里需要先把前面和后面都切开。如 果经常需要这类的操作,可以选择使用帮助类 React.addons.update,updeep,或者使用原生支持深度更新的库 Immutable。最后,时刻谨记永远不要在克隆 state 前修改它。
  2. 单一的庞大的reducer的拆分
    1. 这块设计也不好做,会让人疑惑
    2. 官方给的demo中直接按state的内容区分,我觉得这样做不好,如果后期有跨内容的情况,就比较奇怪了。官方给的combineReducers方案,也只是减少代码量,本质没有变化,state还是拆分处理,路由还是业务逻辑自己来做。
    3. 解决方案:还是处理一整个state,可以按照约定写reducer类而不是方法,类里按照actionType建方法,架构自动路由并调用。
    4. 以前做java架构,路由一定是架构来调用的,目前感觉各大flux框架都是解决问题不彻底。
  3. 官方建议设计模式:顶层容器组件才对redux有依赖,组件间通过props来传递数据。按照这样设计还是没有解决组件间交互和数据传递的问题。官方react设计建议:react的设计建议:http://camsong.github.io/redux-in-chinese/docs/basics/UsageWithReact.htm
  4. 使用connect将state绑定到component。此处有些黑盒了。
  5. 异步action用来请求服务端数据,利用middleware增强createStore的dispatch后即支持。

Relay

没有时间,没做研究

Marty

没有时间,没做研究

结论

开源架构封装的简单的flux会产生较多的冗余代码。

开源架构封装的复杂的redux,其和RN绑定封装了一些东西,是一个黑盒,不易理解和维护。

介于上述两者之间的开源架构reflux,文档较上述2个少,不知道其可持续性如何。如果一定要用开源架构的话,我觉得他稍加封装是一个较为推荐的选择。

不是特复杂的程序【一般spa的程序会更复杂一些,而RN并不是spa】,这些概念只会增加你的开发难度,并且对后面维护的人要求更高。

我们继续头脑风暴,继续抽象总结一下flux系列框架, flux系列框架干了什么,没干什么,针对开篇提出的问题。

  1. 【解决职责】解耦,分层,谁该干什么就干什么,不许干别的,让代码读起来更有预测性和一致性,方便维护
  2. 【解决通信】继续解耦,采用事件机制解决各层之间通信,采用props传递解决各组件之间通信。

事件系统是关键

flux系列架构解决通信问题的方法是使用事件系统,事件系统中的回调函数是业务逻辑,redux是【store action reducer】,flux是【action dispacher store】。

我们真的需要事件系统吗?

事件系统的好处:

  1. 一个事件可以注册多个回调函数
  2. 各回调函数间没有耦合。

关于1

需要注册多个的这种情况并不多见,不信你去翻看你已经写好的代码,是不是大部分都是注册一个。

关于2

解耦确实很彻底,但是当我需要控制执行顺序,需要等a执行完在执行b,怎么办?ok你可以先注册a在注册b啊。那a要是一个fetch或ajax操 作呢?这时候只能乖乖的在a的请求结束回调函数中进行调用b了。又变成a依赖b了。当然,你可以继续dispatch(b),这就没有耦合了。但是你要知 道注册一个事件是要有成本的,要写action,而且大部分情况根本就不需要注册多个回调,而且这种dispatch的方式,真的不太适合人类的阅 读,dispatch一下,下一步都有谁来执行都不知道,这哪有直接调用来的爽快。

好吧说到这,最后的结论也出来了,不使用开源架构,借助其好的思想,替换其事件系统为面向对象结构,自行封装架构。

架构设计

再次强调:目前仅考虑如何应用于react native

先扣题,针对开篇问题的解决方案如下

交互

  1. 组件对外发布:组件对外只允许使用props来暴露功能,不允许使用接口及其它一切方式
  2. 父子组件间:组件的子组件通过父组件传递的接口来与父组件通信
  3. 兄弟组件间:
    1. 方案1:假设a要调用b,参考第一条的话,其实就是a要改变b的props,那么a只要改b的props的来源即可,b的props的来源一般就是根组件的state。那么根组件就要有组织和协调的能力。
    2. 方案2:利用事件机制,基本同flux架构。略复杂,且我们并不需要事件的特性,本架构设计不推荐。
      #### 职责
  4. root-存放state,组织子view组件,组织业务逻辑对象等
  5. 子view组件-根据this.props渲染view。
  6. 业务逻辑对象-提供业务逻辑方法

根据以上推导,我将其命名为面向对象的ReactNative架构设计,它与flux系列架构的最大的不同之处在于,用业务逻辑对象来代替了 【store action dispatcher】or【store reducer】的事件系统。业务逻辑对象就是一组对象,用面向对象的设计理念设计出的n个对象,其负责处理整个页面的业务逻辑。

以上为推导过程,干货才开始。。。。

面向对象的ReactNative组件\页面架构设计

  1. 一个独立完整的组件\页面一般由以下元素构成
    1. root组件,1个,
      1. 负责初始化state
      2. 负责提供对外props列表
      3. 负责组合子view组件形成页面效果
      4. 负责注册业务逻辑对象提供的业务逻辑方法
      5. 负责管理业务逻辑对象
    2. view子组件,0-n个,
      1. 根据props进行视图的渲染
    3. 业务逻辑对象,0-n个,
      1. 提供业务逻辑方法
  2. root组件,中包含:
    1. props-公有属性
    2. state-RN体系的状态,必须使用Immutable对象
    3. 私有属性
    4. 业务逻辑对象的引用-在componentWillMount中初始化
    5. 私有方法-以下划线开头,内部使用or传递给子组件使用
    6. 公有方法【不推荐】,子组件和外部组件都可以用,但不推荐用公有方法来对外发布功能,破坏了面向状态编程,尽可能的使用props来发布功能
  3. 子view组件,中包含:
    1. props-公有属性
    2. 私有属性-如果你不能理解下面的要求,建议没有,统一放在父组件上
      1. 绝对不允许和父组件的属性or状态有冗余。无论是显性冗余还是计算结果冗余,除非你能确定结算是性能的瓶颈。
      2. 此属性只有自己会用,父组件和兄弟组件不会使用,如果你不确定这点,请把这个组件放到父组件上,方便组件间通信
    3. 私有方法-仅作为渲染view的使用,不许有业务逻辑
    4. 公有方法【不推荐,理由同root组件】
  4. 业务逻辑对象,中包含:
    1. root组件对象引用-this.root
    2. 构造器-初始化root对象,初始化私有属性
    3. 私有属性
    4. 公有方法-对外提供业务逻辑
    5. 私有方法-以下划线开头,内部使用

    todo补充架构图

通用型组件只要求尽量满足上述架构设计

通用型组件一般为不包含任何业务的纯技术组件,具有高复用价值、高定制性、通常不能直接使用需要代码定制等特点。

可以说是一个系统的各个基础零件,比如一个蒙板效果,或者一个模态弹出框。

架构的最终目的是保证系统整体结构良好,代码质量良好,易于维护。一般编写通用型组件的人也是经验较为丰富的工程师,代码质量会有保证。而且,作为零件的通用组件的使用场景和生命周期都和普通组件\页面不同,所以,仅要求通用组件编写尽量满足架构设计即可。

view子组件复用问题

抛出一个问题,设计的过程中,子组件是否需要复用?子组件是否需要复用会影响到组件设计。

  1. 需复用,只暴露props,内部自行管理state
  2. 不需复用,只暴露props,内部无state【因为不会单独使用,不需要setState来触发渲染】

其实, 一般按照不需复用的情况设计,除非复用很明确,但这时候应该抽出去,变成独立的组件存在就可以了,所以这个问题是不存在的。

面向对象的ReactNative架构设计优缺点–todo

按场景分析、验证架构设计–todo

触发view改变的场景【即需要setState的场景】

  1. 回调函数触发【异步】
    1. 组件生命周期事件中or用户操作触发 启动请求服务器
    2. 组件生命周期事件中or用户操作触发,启动定时任务or注册了其它回调函数【比如交互管理器的动画结束事件】
  2. 用户操作触发
    1. view的直接改变【同步】
    2. 仅仅注册回调函数【异步,参考上面一条】

demo代码

此demo仿照redux提供的todolist demo编写。

redux demo 地址:http://camsong.github.io/redux-in-chinese/docs/basics/ExampleTodoList.html

demo截图:


'use strict'


let React=require('react-native');
let Immutable = require('immutable');
let {
    AppRegistry,
    Component,
    StyleSheet,
    Text,
    View,
    Navigator,
    TouchableHighlight,
    TouchableOpacity,
    Platform,
    ListView,
    TextInput,
    ScrollView,
    }=React;

//root组件开始-----------------

let  Root =React.createClass({

    //初始化模拟数据,
    data:[{
        name:'aaaaa',
        completed:true,
    },{
        name:'bbbbb',
        completed:false,
    },{
        name:'ccccc',
        completed:false,
    }
    ,{
        name:'ddddd',
        completed:true,
    }],


    componentWillMount(){
        this.addTodoObj=new AddTodoObj(this);
        this.todoListObj=new TodoListObj(this);
        this.filterObj=new FilterObj(this);
    },


    getInitialState(){
      return {
          data:Immutable.fromJS(this.data),//模拟的初始化数据
          todoName:'',//新任务的text
          curFilter:'all',//过滤条件 all no ok
      }
    },



    render(){
        return (
            <View style={{marginTop:40,flex:1}}>

                <AddTodo todoName={this.state.todoName}
                        changeText={this.addTodoObj.change.bind(this.addTodoObj)}
                         pressAdd={this.addTodoObj.press.bind(this.addTodoObj)} />

                <TodoList todos={this.state.data}
                          onTodoPress={this.todoListObj.pressTodo.bind(this.todoListObj)} />

                <Footer curFilter={this.state.curFilter}
                    onFilterPress={this.filterObj.filter.bind(this.filterObj)} />

            </View>
        );
    },



});






//业务逻辑对象开始-------------------------可以使用OO的设计方式设计成多个对象


class AddTodoObj{

    constructor(root){
        this.root=root;
    }


    press(){
        if(!this.root.state.todoName)return;
        let list=this.root.state.data;
        let todo=Immutable.fromJS({name:this.root.state.todoName,completed:false,});
        this.root.setState({data:list.push(todo),todoName:''});
    }

    change(e){
        this.root.setState({todoName:e.nativeEvent.text});
    }

}


class TodoListObj{

    constructor(root){
        this.root=root;
    }


    pressTodo(todo){

        let data=this.root.state.data;

        let i=data.indexOf(todo);

        let todo2=todo.set('completed',!todo.get('completed'));

        this.root.setState({data:data.set(i,todo2)});
    }
}


class FilterObj{

    constructor(root){
        this.root=root;
    }


    filter(type){

        let data=this.root.state.data.toJS();
        if(type=='all'){
            data.map((todo)=>{
                todo.show=true;
            });
        }else if(type=='no'){
            data.map((todo)=>{
                if(todo.completed)todo.show=false;
                else todo.show=true;
             });
        }else if(type=='ok'){
            data.map((todo)=>{
                if(todo.completed)todo.show=true;
                else todo.show=false;
            });
        }


        this.root.setState({curFilter:type,data:Immutable.fromJS(data)});
    }



}


//view子组件开始---------------------------


let Footer=React.createClass({

    render(){

        return (


            <View style={{flexDirection:'row', justifyContent:'flex-end',marginBottom:10,}}>

                <FooterBtn {...this.props} title='全部' name='all'  cur={this.props.curFilter=='all'?true:false} />
                <FooterBtn {...this.props} title='未完成' name='no' cur={this.props.curFilter=='no'?true:false} />
                <FooterBtn {...this.props} title='已完成' name='ok' cur={this.props.curFilter=='ok'?true:false} />

            </View>



        );
    },


});


let FooterBtn=React.createClass({

    render(){

        return (

            <TouchableOpacity onPress={()=>this.props.onFilterPress(this.props.name)}
                              style={[{padding:10,marginRight:10},this.props.cur?{backgroundColor:'green'}:null]} >
                <Text style={[this.props.cur?{color:'fff'}:null]}>
                    {this.props.title}
                </Text>
            </TouchableOpacity>

        );
    },


});


let AddTodo=React.createClass({

    render(){

        return (


            <View style={{flexDirection:'row', alignItems:'center'}}>


                <TextInput value={this.props.todoName}
                    onChange={this.props.changeText}
                    style={{width:200,height:40,borderWidth:1,borderColor:'e5e5e5',margin:10,}}></TextInput>


                <TouchableOpacity onPress={this.props.pressAdd}
                    style={{backgroundColor:'green',padding:10}} >
                    <Text style={{color:'fff'}} >
                        添加任务
                    </Text>
                </TouchableOpacity>

            </View>



        );
    },


});



let Todo=React.createClass({

    render(){
        let todo=this.props.todo;
        return (
            todo.get("show")!=false?
            <TouchableOpacity  onPress={()=>this.props.onTodoPress(todo)}
                style={{padding:10,borderBottomWidth:1,borderBottomColor:'#e5e5e5'}}>
                <Text style={[todo.get('completed')==true?{textDecorationLine:'line-through',color:'#999'}:null]} >
                    {todo.get('completed')==true?'已完成   ':'未完成   '} {todo.get('name')}
                </Text>
            </TouchableOpacity>
             :null
        );
    },


});


let TodoList=React.createClass({
    render(){
        return (
            <ScrollView style={{flex:1}}>
                {this.props.todos.reverse().map((todo, index) => <Todo {...this.props} todo={todo} key={index}  />)}
            </ScrollView>
        );
    },
});




module.exports=Root;

前端路上的旅行【转】

从步入社会算起到今天,风风雨雨走过了15个年头,望着天空,掐指一算,从事前端已近五个年头。也算三分之一的时间在做前端,也是从事过时间最久的 一份工作。问我为什么能做这么久?其实我也时常问自己,怎么就进前端这个行当,怎么就整了五年。正如网上所言,一入前端深似海。

玩了五年,或许后面还会有一个五年,两个五年,或许…… 未来的路,谁与说不清楚,我只想每天走好自己想走的路。

最近在万能的知乎上常有同学邀请我回答:

  • ”今年26岁,女生,想要学WEB前端开发,0基础?“
  • ”作为一个刚入门的前端爱好者,以后立志成为前端攻城狮的我,应该要学习哪些方面的知识?“
  • ”我想做web前端,怎么学习 ?“

其实我也回答不好。无法很好回答的情况之下,促使自己在思考,思考这些年来在前端路上的旅行。也让我写下了这篇文章。

什么是前端?

什么是前端?大部分指的是Web前端开发,这个词是从网页制作演变过来的,名称上有着很明显的时代特征。在互联网的演化过程中,网页制作是Web1.0时代的产物,那时网站的主要内容都是静态的,用户使用网站的行为也以浏览为主。在这个阶段,网站的内容主要是文字内容和图片为主,制作方法也主要是使用表格拼装。印象中,那时主FrontPage这样的,画个表格,往里面填真图片,文字,就叫网页了。

2005年以后,互联网进入了Web2.0时代,各类似桌面软件的Web应用大量涌现,网站的前端由此发生了翻天覆地的变化。网页也不再只是承载单一的文字和图片,各种富媒体让网页的内容更加生动,网页上软件化的交互形式为用户提供了更多好的使用体验,这些都基于前端技术实现。

在这个时代,给前端冠上的名称也多了,”做网站的“、”美工“、”网站设计师“、”切页面的“、”前端工程师“等等。同时技术也更为复杂化了,难度也更大了,同时技术含量也高了,那么随着这些变化,从事前端的人员也有一个专业的名号了”前端工程师“,收入也随着水涨船高了。

技术发展是日新月异,现在业内人员开始提出Web3.0的 概念。最常见的解释是,网站内的信息可以直接和其他网站相关信息进行交互,能通过第三方信息平台同时对多家网站的信息进行整合使用;用户在互联网上拥有自 己的数据,并能在不同网站上使用;完全基于Web,用浏览器即可实现复杂系统程序才能实现的系统功能;用户数据审计之后,同步于网络数据。那么问题就来 了?在Web3.0的时代,前端又是什么呢?所谓的”前端工程师”又能做些什么呢?又能给Web带来什么呢?这些都值得我们一起思考。

我是怎样变成前端工程师?

我是怎样变成一枚前端工程师?其实我一直都是一枚”伪前端工程师“,充其量就是一枚会做Web页面的从业人员,不敢以工程师自居(有辱工程师这一高尚的称谓)。其实我更喜欢把自己称为”页面仔“,或者Web的美容师。或许你又会说:”大神“你谦虚了。其实不是的,当你坚持阅读完后面的内容,你也会有这样的想法。

什么是前端工程师

前端工程师,也被称作Web前端开发工程师(在一些公司又将其分为:前端工程师的初级、中级、高级、专家和资深等)。他是随着Web发展,细分出来的行业。

简单点定义前端工程师:运用前端技术,实现体验的良好传达。简单点理解,就是使用Web的前端相关技术,实现一个用户体验良好的网站。而Web前端开发技术主要包括三个要素:结构层HTML,表现层CSS,交互层JavaScript(也有人称之为行为层)。

对于前端工发工程师而言不仅要掌握基本的Web前端技术,网站性能优化,SEO和服务端的基础知识,还要学会运用各种工具进行辅助开发以及理论层面的知识,包括代码的可维护性、组件的易用性、分层主义模板和浏览器分级支持等。

就此而言,我能对上号,就是对HTML和CSS有一定的了解。也就称不上是一枚前端工程师。这也就是为什么一直强调自己不是Web工程师,而只是一枚Web的美容师。不过对于我是不是什么,并不太重要,既然扯了这么多,咱们开始来聊点实际的,有用的东西。

前端工程师要掌握的技术

Web前端技术包括三个要素:HTML、CSS和JavaScript,但随着RIA的流行和普及,Flash/Flex、 Silverlight、XML和服务器端语言也是前端开发工程师应该掌握的。Web前端开发工程师既要与上游的交互设计师、视觉设计师和产品经理沟通, 又要与下游的服务器端工程师沟通,需要掌握的技能非常多。这就从知识的广度上对Web前端开发工程师提出了要求。

从网上扒了张图,都说一图胜千言万语:

前端工程师要掌握的技术

作为前端开发者,首先要掌握的就是HTML+CSS,有了这两项最基础的知识,就能得心应手的将设计师的Web页面切出来(将PSD设计图转换成 Web静态页面)。但这还是不够的,还需要能熟练的处理各浏览器的兼容。为了能更好的要求自己,尽量要让自己的Web页面符合W3C标准和语义化的规范。 这方面虽然不是硬性的要求,但是做好这两者很有必要,也具有较大的难度。

充分掌握了HTML和CSS方面知识后,接下去要把JavaScript拿下,这也是前端工程师必备技能之一,也算是较难的一部分(我至今写不了 JS,所以…你懂的)。只懂HTML、CSS和JavaScript其中一个或两个还不行,你必须对这三门语言都很熟悉。也不是说必须对这三门语言都 非常精通,但你至少要能够运用它们完成大多数任务,而无需频繁地寻求别人的帮助。

优秀的前端工程师应该具备快速学习能力。推动Web发展的技术并不是静止不动的,没错吧?我甚至可以说这些技术几乎每天都在变化,如果没有快速学习 能力,你就跟不上Web发展的步伐。你必须不断提升自己,不断学习新技术、新模式;仅仅依靠今天的知识无法适应未来。Web的明天与今天必将有天壤之别, 而你的工作就是要搞清楚如何通过自己的Web应用程序来体现这种翻天覆地的变化。

优秀的前端工程师需要具备良好的沟通能力,因为你的工作与很多人的工作息息相关。在任何情况下,前端工程师至少都要满足下列四类客户的需求。

  • 产品经理——这些是负责策划应用程序的一群人。他们能够想象出怎样通过应用程序来满足用户需求,以及怎样通过他们设计的模式赚到钱(但愿如此)。一般来说,这些人追求的是丰富的功能。
  • UI设计师——这些人负责应用程序的视觉设计和交互模拟。他们关心的是用户对什么敏感、交互的一贯性以及整体的好用性。他们热衷于流畅靓丽但并不容易实现的用户界面。
  • 项目经理——这些人负责实际地运行和维护应用程序。项目管理的主要关注点,无外乎正常运行时间(uptime)——应用程序始终正常可用的时间、性能和截止日期。项目经理追求的目标往往是尽量保持事情的简单化,以及不在升级更新时引入新问题。
  • 最终用户——当然是应用程序的主要消费者。尽管我们不会经常与最终用户打交道,但他们的反馈意见至关重要;没人想用的应用程序毫无价值。最终用户要求最多的就是对个人有用的功能,以及竞争性产品所具备的功能。

如果你想把自己变得更为强大,处于不败之地,那你需要根据下图去提高自已:

前端工程师要掌握的技术

看到这里,你是不是对“前端工程师”有更深入的了解呢?当然,你看到这里也,或许方向更明确,或许更惧怕。因为有很多前端人员就是因为这些原因离开前端的战场,转站其他的IT岗位。

回到当初的问题

既然你知道了什么是前端工程师,前端工程师要掌握些什么?接下来回到前面自设的问题——我是怎么变为一名”前端工程师”?

从我从业的年龄和从事前端工作时间推算,我是典型的一名半路出家的和尚。五年前,因为种种原因,放弃自己的专业投入了互联网的阵营,学了HTML+CSS,接下来顺理成章的就成了一名”前端“,也可谓是一入前端深似海。

借此想告诉一些想转行做前端的同学?在转行从事前端这份工作,需要经历一个很长的时间跨度,你需要花费更多的精力、做更多的项目,进行更多的反思和总结才能理解某个知识点的本质。当然,如果你有这方面的天赋,或许你不要经历这些。

同时你还要摆脱很多思维定势和禁锢,当然,如果你有一位师傅带你入道,那你是幸运儿。但不管怎么样,我始终认为应当秉承兴趣第一的原则,不管你是误打误撞,还是意欲为之,兴趣能促使你有更多的动力去学习,去做。你也能做得更好。

“我对Web开发人员最大的建议就是:热爱你的工作。热爱跨浏览器开发带来的挑战、热爱互联网技术的种种异端,热爱业内的同行,热爱你的工 具。互联网发展太快了,如果你不热爱它的话,不可能跟上它的步伐。这意味着你必须多阅读,多动手,保证自己的才能与日俱增。下了班也不能闲着,要做一些对 自己有用的 事儿。可以参与一些开源软件的开发,读读好书,看看牛人的博客。经常参加一些会议,看看别人都在干什么。要想让自己快速成长,有很多事儿可以去做,而且付 出一定会有回报。“ ————@N.C.Zakas

其实做前端的很多同学都和我一样是半路出家。拿我来说,当初选择做前端主要:

  • 讨厌现在的工作,想换一份能更轻松的工作,也能帮我改善生活的工作
  • 前端入门门槛简单,学点HTML+CSS就能开始找份工作,边做边学
  • 前端是一门所见即所得,你做了就能看到效果,能清楚的知道自己做得对还是错

随着时间的久已,做别的也做不了,也不想做。也就坚持一直在做,不过今天的我,把前端不再当做一份工作,而是将其当做自己的兴趣爱好。所以我自我感 觉越来越良好,越来越轻松,虽然还有很多技术,很多知识要学习,但我一直坚持在学习,让自己能紧跟时代的步伐,尽量不让自己过早的淘汰。

半路出家的人员心态都比较走两个极端,一是看中他的前景;二者是讨厌原工作。我自己就是属于二者。但不管是你已转行了,还是将要转行做前端,我都建议您静下心来思考下面几个问题:

  • 我能做什么?
  • 我不能做什么?
  • 我的优势是什么?
  • 我的劣势是什么?
  • 做前端对我有何好处?
  • 做前端我要付出何种代价?
  • 我能不能坚持做下去?

如果你不能好好的,清楚的回答这些问题,我建议你还是不要轻意转行做前端。

如何学习前端?

学习对于每一个人都有自己独特的方式,我也不另外。在此我不能说我的学习方法就是好的,我只是想借此机会说说自己是如何学习前端技术的。

做为一位半路出家的和尚而言,在还没开始学习前端之前,我内心是很惧怕的,我怕我隔了十年之后,还能不能静下心来读书。还能不能读懂。当初我也是抱着试试看的心态,因为我不在年轻,因为我有家庭,因为有各种压力。

既然选择了,我就放下了。抱着《HTML+CSS从入门到精通》和《CSS Cookbook》:

HTML+CSS从入门到精通

CSS Cookbook

开始了前端的学习。在整个学习的过程是一件痛苦的过程,在只看书的情况之下,并不清楚自己是否看懂了,是否掌握了。基于这种情况之下,我在只知道HTML标签和如何使用CSS属性情形之下,我开始了不断的仿站。

与其说是仿站还不如说是抄。印象中最早就是拿着WordPress官网, 借着Firebug工具(这是一个神奇的工具,让我少了很多烦恼)练习。就是先抄其HTML,然后抄其CSS。抄一段,在浏览器刷新一次看效果,就是这样 的一个过程,让我找到了学习的自信,我觉得我自己能学会。就这样一段时间后,我自己能独立做出一个外表长得和原网站一样的站(没有任何的数据、没有任何的 交互,没做任何的兼容)。

这样是不够的,接下来,为了自己能更多的了解HTML+CSS,我一直在走着同样的路,在Website Templates寻找喜欢的,觉得简单的模板,开始仿做。但是对于前端,需要将PSD设计图转换成Web页面,那前面的过程是不够的,需要尝试将PSD切出Web页面。互联网是成能的,我在网站下载了很多PSD设计图,然后将其转换成Web页面。

国外优秀的UI设计资源库

国外优秀的UI设计资源库

经过一段时间之后,拿着仿出来的凡客诚品去找工作,值得幸运的是,有了一份前端的工作。在这个工作当中,让我带来更多的机会,让我能更好的去理解和学习的场景。

有了一年工作经验之后,我需要的是开始沉淀自己。我在开始思考:

  • 我掌握了什么?
  • 哪些似懂非懂?
  • 哪些不懂?

有了这三份清单,我又开始有了新的目标,针对清单上的去做总结,去把似懂不懂和不懂的搞懂。于是,我有了自己的第一个博客——W3cplus。在这个博客上记下了我学习的历程和积累的前端知识。更让我值得庆幸的是,这个网站让我认识了更多的同行朋友,也给我带来了更多的机会。

其实这些记录的都是一些零散的知识点,如果要系统的拿下一个知识,那还是不够的。在这种情形之下,2012年开始了两年漫长的写书过程,在这两年时间当中,我一直在学习和整理自己掌握的CSS3知识,这就是《《图解CSS3:核心技术与案例实战》

《图解CSS3:核心技术与案例实战》

在这个过程,我学会了去读W3C规范,也让我更清楚的了解了CSS。更值得庆幸的是,我学会了如何搜索自己需要寻找的知识,我想这才是最值钱的一部分吧。

看到这里,或许你会认为我好傻,其实我真的好傻。但我一直认为,既然自己没有天赋,就需要付出比常人更多的时间。

简单点归纳:

  • 多看:多看书,多看规范,多看优秀的网站,多看优秀的代码
  • 多问:多问几个为什么?多问几个出处?多问几个原由
  • 多做:多做练习,实战出真理;多做,才能有更多经验
  • 多想:多思考几个为什么?
  • 多总结:多总结看到、想到的、听到的,这些沉淀下来的都是你自己的

我的前端瓶颈

记得曾经有位前辈曾说,如果你一年、两年都在折腾同一样的东西,那意味着你没有任何的进步。其实我现在就面临这样的现象,几年来一直在折腾CSS这样的独技,而对于其他的并未有掌握,或者说了解。

这让自己显得迷茫,欲想得,欲离得甚远。特别是当今的前端,如果仅仅掌握HTML和CSS是越来越不够了,在今年,在这个新的团队当中,我越来越感 觉到,掌握的东西太少,需要的学习的东西太多。因为想做的事情也太多。这也让我有了更大的压力,人家都说压力就是动力,有了动力就有冲劲。

不过,由于自己所撑的少,什么都想学,这也让自已感到迷茫,似乎没了方向。这也是我最近在思考的事情,我应该如何往下走,我应该如何不被前端的大潮迷失了自己,失去了方向。可是我到今天还没有想清楚,但我希望有一天我能想清楚,我也更希望这样的一天能早点到来。

总结

无论从哪个方面讲,我都觉得前端工程师是计算机科学职业领域中最复杂的一个工种。绝大多数传统的编程思想已经不适用了,为了在多种平台中使用,多种 技术都借鉴了大量软科学的知识和理念。成为优秀前端工程师所要具备的专业技术,涉及到广阔而复杂的领域,这些领域又会因为你最终必须服务的各方的介入而变 得更加复杂。专业技术可能会引领你进入成为前端工程师的大门,但只有运用该技术创造的应用程序以及你跟他人并肩协同的能力,才会真正让你变得优秀。

【过程改进】如何制定外部评价整改计划?

软件外部评价,是组织进行软件过程改进的一大契机。

由于人性的弱点,自己很难发现自己的问题。所以说,外来的和尚好念经,也是有它的道理。

通过外部评价,由外部专家评估组织的过程,标识出过程存在的问题,这就是非常难得的过程改进时机。

可是,如果不好好策划如何针对外部专家发现的问题进行整改,这种难得的机会也会白白浪费,组织的过程改进不会取得什么好的效果。

那么,怎么才能做好这个整改计划呢?

从CMMI的集成项目管理过程域来看,制定过程改进行动计划的前提就是先要确定过程改进的时机,而过程改进时机包括3个专用实践,分别是建立组织的过程需要、评估组织的过程和标识组织的过程改进。

外部专家评价能够为后两个专用实践的提供部分内容。但是,外部评价提供的内容不是这两个实践的全部。除了外部评价,评估组织的过程还包括组织的内部评估;标识组织的过程改进还包括从项目组获取的经验教训,从组织外部获取的优秀实践等。

更重要的是,外部评价无法覆盖第一个实践——建立组织的过程需要。而组织的过程需要,才是组织过程改进的目标和方向。

所以,要做好外部评价的整改策划,就是要在这个目标下进行权衡,所有的决策均应以满足这一目标为判断准则。

目标,对于一件事的成功有着非常重要的意义。人们在做一件事的过程中,常常沉浸在做事当中,却忘了最初的目标,忘了为什么做这件事。所以,可能做了很多无用功,可能做得偏离了方向。

要做好过程改进,就不要忘记了自己组织的真正需要。

那么,如何建立组织的过程需要呢?

组织的过程需要,首先来自于组织对软件工程发展的期望,来自于组织对各个过程域的指导方针。具体来说,除了组织方针之外,组织的过程需要还来自于组织对软件业务和能力的发展规划,这个规划通常是在组织的五年战略发展规划中。

由于这个规划通常是宏观的表述,所以需要过程改进组(EPG)成员对这个组织需求进行开发、分析和分解,将其从规划转变成每个过程域的改进目标,并分配到每年的年度过程改进计划当中。

如果己经完成了外部评价,又没有在过程改进计划当中分解出清晰的各个过程域的年度改进目标,EPG至少要能够对收集到的与过程改进有关的信息(如发展规划、改进计划、其它组织的先进经验、试点项目反馈的经验教训等)进行分析和讨论,确定当下组织需要解决的主要问题,以此作为改进的过程需要。

有了具体的过程改进目标,接下来进行后续两个实践。

“评估组织的过程”这个实践,不仅要有外部评价,还应进行组织的内部评估,以及其它渠道反映的组织过程的不足。比如质量体系评审和产品质量评审。

最后,将上述的评估过程发现的问题进行分类汇总并根据已经建立的组织的过程需要,确定各个问题项的重要程度以及解决问题项的优先级。

优先级的确定,要考虑该项改进对组织年度过程改进目标实现的重要意义以及实施该项改进所需要的投入多少。对组织年度过程改进目标实现非常重要,而所需投入不多的改进项,其优先级应该最高。

确定了改进项之后,接下来就是具体的改进策划。

首先要按照问题项类别,将改进策划的任务分配给合适的人员。比如管理类过程域的改进项,适合的人员至少应当具备2个条件:一是熟悉标准对该过程域的要求;二是熟悉组织的管理流程,有一定的管理经验。对于工程类过程域的改进项,适合的人员至少也应当具备两个条件:一是熟悉标准对该过程域的要求:二是有丰富的工程经验,在他以往完成的该过程的主要工作产品的质量较高。

其次,每个改进策划都应考虑以下几个方面:

1)过程文件。考虑该项改进是否需要修订过程文件,修订哪些内容;

2)资源。考虑该项改进需要投入的人员,以及涉及到哪些利益相关方;考虑该项改进是否需要增加新的设备和工具以及对工具的改进;

3)培训。考虑该项改进是否需要内部、外部培训,以及培训讲师、培训内容、培训方式等的建议;

4)改进步骤。对于一些需要投入较大的改进项,应当考虑分步实施,建立一个迭代计划。

最后,就是对过程改进的策划进行评审,获得各利益相关方的承诺。

伟大程序员的十个特质

如何才能成为一名伟大的程序员,历来都是大家讨论的焦点话题之一。网络上已经有很多有关这方面的文章,今天分享的则是编程培训机构CodingDojo网站上的一篇文章,本文从伟大程序员的10个共同特质出发,对这一焦点话题进行了阐述。——EarlGrey@编程派

如果你紧跟最新的编程语言和框架,那就太棒了:你已经掌握了写出良好代码的技术能力。但是要想在岗位上真正脱颖而出,你还必须具备一些特质。下面就 是成为一名优秀程序员所必须具备的10个特质。(我们知道下面这个列表并不全面,所以读者可以在评论区给出自己心中优秀程序员的特质。)

Guido van Rossum:伟大程序员之一

Python编程语言的发明者:Guido van Rossum

1. 发展全面。

深入了解一门技术虽然很好,但是现实世界中的问题从来都无法仅靠一种技术就能够解决。即使别人雇佣你为专业技术人员,你仍然需要明白你所掌握的技术 如何与组成应用生态系统的其他软件、硬件和网络进行交互。你还需要以多种不同的方式为项目作出贡献,只要项目需要更多支持,就及时给予帮助。

2. 享受解决难题的过程。

应用开发并不是一蹴而就的。在开发过程中,要搞清楚代码无法编译的原因、故障为什么发生以及学会如何解决生产中的问题,这些都需要解决问题的能力, 以及坚信可以找到解决办法,坚持不懈不放弃的信念。如果你能在压力之下解决难题,那就更好不过了——当系统瘫痪的时候,你可以想象管理层可是会在你解决问 题的时候,一直死盯着你的。

3. 热爱学习。

技术在不断的演进。你今天所使用的工具和语言并不一定会是你明年使用的工具,更不用说十年之后了。因此,你需要不断地学习新技能,这样才能参加未来的项目。你的老板可能会提供持续的培训,但是最优秀的程序员会自己花时间自学。

4. 善于沟通。

程序员的工作并不仅仅是和技术打交道。程序员还需要和商业用户交流,理解他们对应用的需求。他们还经常需要输出技术文档,因此写作清晰也是很重要的——即使只是输出一份进度报告。

5. 自信。

开发一个系统并不只有一种方法。不管你的想法多么的出色,如果你藏在心里,那么这些想法也是毫无价值。最优秀的程序员对自己的想法非常自信,在设计 讨论阶段敢于说出自己的意见,这样甚至可以左右应用的架构设计。要增强自己的信息,可以先提一些小建议,而不是一开始就提议重新设计整个应用。

6. 对商业的好奇心。

公司通过技术解决商业问题。你对公司的业务了解的更深,你就能越容易地理解公司想要解决的问题,更顺利地开发出帮助公司发展的解决方案。你应该充分 利用与商业用户交流的机会,询问他们在工作中所面临的困难。如果你真正对商业感兴趣,你可以参加一些课程,甚至试着获得一些商业领域的资格认证。

7. 注重团队合作。

电影里通常会美化某一个程序员的作用,计算机专业的学生也是常常自己一个人完成作业,但是现实世界中的项目都是需要团队实现的。因此,一名程序员能 否与同事融洽相处也很重要。你需要能够与不同能力水平的人打交道,尊重他们的不同意见。如果可能的话,尽量了解你的队友,把他们当作你的朋友,而不是技术 人员。时常聊一聊项目之外的事情,也会增进同事之间的关系,工作上的合作也会更容易。

8. 明白截止日期的重要性。

最优秀的项目经理在安排项目截止日期时,会考虑项目开发人员的意见,尽管有时候外部因素会缩短项目日程。不管怎样,只要你同意完成一项任务,你就要 全力以赴,在截止日期之前完成工作,即使这意味着你要加班。当然,你没有必要为了公司和工作放弃自己的个人生活,但是你所展示出的对完成项目的决心和努力 会给同事留下积极的印象。

9. 适应性强。

项目和优先级会因为多种原因不断变化,因此程序员需要能够随时转变,专注在当前最重要的事情上面。这些可能细微、暂时的更改,也有可能是重大且长期 的变化。如果是暂时的,那请务必做好记录,好在事情稳定下来之后可以回归到正常的开发中。如果是长期的,就要花时间去理解新的场景,研究如何融入进去。这 也可能会提供帮助你达成目标的新机遇。不管发生什么情况,要时刻保持专业的水准,不要面对困境就自我沉沦。

10. 把自己当成产品的主人。

严格来说,你编写完成可以顺利编译、通过测试用例的代码之后,你的工作就结束了。如果你能够继续参与产品生命周期的其他阶段,那你就十分出众了—— 这意味着,你要愿意协助测试、开发、培训用户和解决开发中的问题。尽管这些工作对你的工作更有好处,但实际上你获得的却更多,因为你亲自看到并体验了你写 的代码在实际生产时的效率和作用。接下来,你就需要充分利用这份经验心得,让自己的下一个应用变得更棒!

An informal introduction to Lambda Calculus.

Part 1

It was two or three years ago. I was reading some Python code when I came across a λ-expression for the first time. My immediate reaction was confusion on why that was out of sync with rest of the syntax.

  1. Why use ‘lambda’ and not ‘function’?
  2. Why are parameters not enclosed in a bracket?

If you haven’t done Python, this is how a λ expression in Python looks like,

filter(lambda x: x % 3 == 0, [1, 2, 3, 4, 5, 6, 7, 8, 9])

Anyway I didn’t bother to read a lot into all those and just carried on with the code.

I am a big fan of Paul Graham’s essays. After reading this one(you should too) where he talks about how his choice of programming language,lisp, was his secret weapon, I was determined to explore functional programming which lisp epitomises. What is so special about functional programming that some really good programmers are speaking highly of it. Why is everyone betting big on functional programming.

I started exploring the functional world, started learning the functional programming concepts(immutability, statelessness, closures, TCO, etc,.) using mostly Lisp/Clojure. One thing I noticed when reading books or blog posts on FP is that there are always some mentions of λ calculus in them. After a bit of research, it became clear that functional programming has it roots in λ Calculus. Hence an exploration of functional programming would be incomplete without studying λ Calculus.

Some History

An influential mathematician of the early 20th century, David Hilbert, published his famous list of 23 problems in mathematics. The problems were unsolved at that time and were the kinds of problems whose solutions would lead to the advancement of mathematics. Later he posed another challenge which is famously known as Entscheidungsproblem (Decision problem).These problems of Hilbert had promoted vigorous investigation into the theory of computability. The two computing models that came out of that time were Alonzo Church’s Lambda Calculus and Alan Turing’s Turing Machine.

Hilbert’s Decision problem asks if is it possible to devise an algorithm to solve any problem. Alan Turing answered NO to the problem. Of course, saying no is not enough. You have to prove it. Turing created an abstract computation model called Turing Machine as part of his proof. You can think of Turing Machine as a mini computer. Any thing a computer does, a Turing Machine can do.

Turing showed that it was impossible to compute whether a turing machine would halt for any program-input pair. To explain further, consider the below programs.

1) a = 1 + 3
2) while (True): print ‘hello’

While the first statement terminates, the second one doesn’t. These are trivial programs, hence deducing if they will terminate is easy. But as the programs get complex, it might prove to be a difficult task. Turing showed that it is not only difficult, but impossible.

Around the same time Turing was working on this, his advisor Alonzo Church was working on another model of computation called λ Calculus which is the subject of this post. Actually, Church’s work started long before Turing’s.

Lambda Calculus

Lambda Calculus is conceptually the simplest programming language in the world.

All you have are functions. Oh, they are all anonymous.

A function should take only one ONE argument.

A function should return a value.

That’s it. Three simple rules. Two keywords ‘λ’ and ‘.’. And you can model any problem. Any problem a computer/turing machine can solve, you can solve them using λ Calculus, not in a straight forward way though as you will see.

Here is the grammar of the language,

<expression>   := <name> | <function> | <application>
<function>     := λ<name>.<expression>
<application>  := (<expression> <expression>)

Looking at this, you will realise that everything is an expression, λ expression.

Lets see few λ expressions,

1) λx.x
2) λfunc.λarg.(func arg)
3) ((λfunc.λarg.(func arg) λx.x) y)

First expression is just an identity function. It returns the value it gets.

The second expression sure doesn’t look all that great. But it gives a good idea of the things to come. If you dissect it, it is a function that takes the argument funcand returns another function. This returned function in turn takes the argument arg and returns the application of function func to argument arg.

Function application

The third expression illustrates the usage of second expression. It also gives an opportunity to discuss how function applications work. An function application expression is of form (f arg). To evaluate this expression, we have to first evaluate the expression in the left side.

In the third expression, the left side is

(λfunc.λarg.(func arg) λx.x)

This expression itself is a function application. It’s left side doesn’t need more evaluation. Now to apply a function to its argument, just substitute the argument in function’s body. After substitution, we have the below result.

λarg.(λx.x arg)

After this step, the third expression becomes,

((λfunc.λarg.(func arg) λx.x) y) --> (λarg.(λx.x arg) y)

Now the reduction of the resultant expression will proceed as follows,

(λarg.(λx.x arg) y)
(λx.x y)
y

Yes, the third expression evaluates to y.

What’s next…

Now that we have covered the basics of λ Calculus, we can explore some interesting things in later parts.

We will be modelling an interesting problem in λ Calculus and study in which ways λ Calculus has influenced some of the significant functional programming languages of today. Stay tuned.

TRIVIA:

Y-Combinator. Who haven’t heard of it? Have you ever thought about what it could possibly mean. Surprise, “Y combinator” is a concept from Lambda Calculus. Paul Graham picked it up as the name for this startup incubator.

“Y combinator” is a higher-order function which takes a single argument, which is a function that isn’t recursive. It returns a version of the function which is recursive.

探秘计算之源,你或许能理解计算机强大抽象能力的原因所在

想象你正在挑选新车,这时推销员对你说到:「您知道这辆车不仅能在路上开吗?」

你回应到:「哦?」

「对的,你还可以用它干别的事情。比方说,它能折叠成一辆漂亮的自行车。并且它也变成一架一流的飞机。当它在水下的时候,它就是一艘潜艇。而且它同样是一部宇宙飞船。」

你自然会认为推销员在开玩笑。但是对我们的电脑,我们却认为它的相对灵活性理所当然。有了这么一个机器,我们可以用飞行模拟器飞过自由女神像,用电子制表软件制作财务预测,在脸书上和朋友聊天,还能做许多其它事。它几乎就和汽车、自行车和太空飞船的合体机器一样令人惊叹。

计算机的两个特点让这种灵活性成为可能。首先,计算机是可编程的。也就是说,我们可以通过输入一条恰当的指令来改变计算机的行为。其次,计算机是通用的。只要计算机有足够的内存和时间,我们可以通过恰当的程序让计算机执行任意的算法流程。

可编程性和通用性的观念嵌入我们文化如此之深,以至于小孩子都对它们很熟悉。但它们曾是历史上的重大突破。图灵在1937年的一篇论文中形成了这两个概念,图灵认为一个通用的可编程的计算机可以计算任意的算法流程。图灵所描述的机器——图灵机——是现代计算机的祖先。

如果你对这台计算机有了透彻的理解,那么你就理解了所有的物理过程。

为了证实他的论断,图灵需要证明他的通用计算机能运行任意的算法流程。但这并不容易。在图灵的时代,还没有正式的算法这个概念,也没有一个严谨的数学定义。然而,数学家已然发现许多可以用来测试的特定算法,比如说加法、乘法以及判定一个数字是不是质数。对图灵来说,演示这些已知算法可以在他的通用计算机上运行是颇为简单的。但是,这并不够。图灵还需要更强有力的证据证明,他的通用计算机可以运行任意算法,包括所有可能在未来发现的算法。为此,图灵发展了一系列想法,每个都可以为他的理念提供非形式的辩护。然而,最终,他还是因自己发表的言论缺乏正式定义而感到不适,他说:「所有的论证在本质上势必会求助于直觉,因此,会在数学上令人不满。」

1985年,物理学家David Deutsch在理解算法本质上取得了另一个重要的进步。他观察到,算法流程必须由物理系统执行。这个流程能够以多种方式发生——一个人用算盘运算两个数的乘法,显然不同于一个硅片运行飞行模拟器。但是,二者都是物理系统的案例,也都服从相同的物理法则。由这点出发,Deutsch陈述了如下原理(我会用他的原话——尽管用语是专业的,它还是很容易理解的,而且看到原来的表述颇为有趣):

每个有限的可实现的物理系统,都能由一台通用型计算机通过有限的手段来完美模拟。

换句话说,你可以用一台通用计算机去模拟任意的物理过程。这是一个令人惊叹的颠覆性观点:一台计算机能有效的蕴含物理法则支配下的所有东西。想要模拟一颗超新星吗?或是黑洞的形成?抑或是大爆炸?Deutsch的原理告诉我们,通用计算机能够模拟它们。也可以说,如果你对这样一台机器有了透彻理解的话,你也就理解了所有的物理过程。

Deutsch的原理要比之前图灵非正式理论更合理。如果Deutsch这个原理是正确的,那么,就会自动证实通用计算机能模拟任意的算法流程——因为算法流程本质上是物理过程。你可以用通用计算机模拟算盘上的加法、在硅片上运行飞行模拟器,或是做其它你想做的事。

而且,不同于图灵的非正式论证,Deutsch的原理是可证实的。特别是我们可以想象用物理法则推断算法的实质。那将为图灵对物理法则的非正式论证提供支持,也为有关何为算法的观点提供了一个更坚实的基础。

这样的尝试将有助于在两个方面修正Deutsch的原理。首先,我们必须将量子计算机纳入我们的计算机概念中。这不会在原则上改变可以被模拟的物理过程的类别,但是,它可以让我们快速有效的模拟量子过程。这点很重要,因为量子过程在传统计算机上的模拟经常如此之慢以致于几乎不可能实现。其次,我们必须放宽Deutsch的原理——我们要允许特定相近程度上的模拟而非完美的模拟。这是系统模拟内涵的一个弱概念,但它对原理的应用几乎是必要的。

这两个改动让Deutsch的原理变成了:

每个有限的可实现的物理系统都可以由一台通用型(量子)计算机通过有效的手段在近似程度上有效的模拟。

至今还没有人能成功的从物理法则中推衍出这种形式的的Deutsch原则。部分原因是,我们还不知道物理法则是什么!尤其是我们还不知道怎么将量子力学和广义相对论统一起来。也因此我们对是否能用计算机模拟涉及到量子引力的过程(比如说黑洞蒸发)还抱有疑问。

但是,即便没有量子引力理论,我们还可以问:计算机是否能模拟现代物理的最佳理论——粒子物理标准模型(Standard Model of particle physics)以及广义相对论。

研究者们正在积极的回答这些问题。在过去的几年,物理学家John Preskill和他的同事们已经演示了怎么用量子计算机有效的模拟几个简单的量子场理论。你可以认为这些是粒子物理标准模型的原型。它们没有含括标准模型的全部复杂性,但是却拥有它的一些基本理念。尽管Preskill和他的同事没有成功的解释该怎么模拟完整的标准模型,他们却克服了这个任务会遇到的许多技术阻碍。接下来几年,人们可能会发现Deutsch原则对标准模型的证明。

广义相对论的情况则更模糊。广义相对论介绍了奇点,而它撕裂时空的方式还没有完全被人所理解。虽然有数名相对论学者发展出许多模拟特定物理场景的技术,然而据我所知,人们尚未对怎么有效的模拟广义相对论做出系统的、彻底的分析。它是个有趣的开放问题。

博学者Herbert Simon在他的书 《The Sciences of the Artificial》中辨析了自然科学(比如说物理系和生物学)——我们在其中学习自然发生的系统,以及人造科学(像是计算机科学和经济学)——我们在其中学习人造的系统。

我们初始印象可能会认为,人造科学是自然科学的特殊部分。但就如Deutsch原理所表明的那样,人造系统的组成部分(像是计算机)和自然发生系统的一样繁杂。我们可以想象,不仅用计算机去模拟已有的物理法则,甚至有可能用它去模拟可能的物理实体。正如计算机科学家Alan Kay所说的那样:「在自然科学中,自然给了我们一个世界,我们仅是去发现它的原理。而在计算机中,我们可以用法则去填充它,创造一个新世界。」Deutsch原理提供了一个连接自然科学和人造科学的桥梁。我们就快要证明这个基础的科学原则了,这令人感到兴奋。

技术人员在大公司能学到什么

我在小公司待过、也在大公司待过、还作为小公司的咨询顾问在大公司待过很长一段时间,目前还在大公司待。对于个人成长,大公司能给你哪些小公司很难给的机会?这是本文想讨论的主题。

技术人员在大公司要面对的问题

个人成长,方法大致是两种,第一是主动学,现在互联网这么开放,IT行业中的知识,只要你想学,几乎没有找不到的资料。基本上,稍微靠谱点的技术人才,都具备主动学习的素质,然而这种学习方式,无论是看书、读博客、上在线课程…… 都有个非常明显的缺点,就是缺乏对问题的直观体验,几年前我看《Java Concurrency In Practice》,囫囵吞枣,表面上懂了,实际上压根没理解。近期当我面对一个比较典型的并发问题的时候,再翻出那本书,忍不住一口气读了几十页,因为实在是太对胃口了!所以第二种学习方法往往更为重要,那就是:面对问题,解决问题,这是一种基于体验的成长,比基于纯理性的记忆理解,深刻得多。

所以,有哪些问题,大公司需要面对?小公司不需要面对?我总结下基本是三个问题:

1、大公司服务的用户数量级相比小公司不在一个层次。

2、大公司需要考虑如何保持数百数千程序员高效工作。/3、大公司处理的业务往往非常之复杂。

当然上述几点并不总是正确的,比如现在会有一些小公司服务千万级别的用户,也需要面临类似的技术问题。但大体上就是这些问题,大公司必要面对,小公司在早期是不需要面对的。

上述三个问题实际上是 Scalability 的问题,具体我推荐大家看《The Art of Scalability》的详细阐述。解决上述三个问题,需要技术人员具备怎样的能力?

三个问题要求你学什么?

第一个问题,如果面对百万级、千万级的用户,是被大家讨论最多的,具体的技术会涉及到:无状态应用、负载均衡、分布式缓存、分布式队列、高性能Web服务器、数据分库分表…… 在大公司,也许根本轮不到你去开发分布式缓存,但只要你留点心,就能很快理解在什么情况下该用分布式缓存,它能带来多少性能提升,命中率还有多少提升空间,等等。这一块,是大家面试的时候比较喜欢问题的,没做过的,都觉得很酷很牛逼的样子,经历过了,感觉其实也就那样,关键是你遇到问题了,并且用这些技术解决了。

第二个问题,如何管理数百人的研发部门,更多的是被很多纯管理职位的经理在讨论。普通程序员,面对由于跨团队跨部门沟通所带来的消耗,10个程序员估计会有11个骂娘;然而问题始终是客观存在的,我现在的日常工作一直遇到类似的问题,我也问过 Facebook 的工程师,人家也坦然承认,较之与

Facebook 早期,他们现在研发的效率也的确慢了很多。

保持自己工作高效,并不是个特别困难的问题;保持2-3人小团队工作高效,也不难,只要大家志趣相投、目标一致基本就可以了;10人左右的团队,就需要聪明的管理者花许多时间去理解大家的想法并协调。当然,愚蠢的管理者只会开一大堆无意义的会,刷点存在感,传达点上面给的压力。团队规模再扩大,带领上百人团队的中层管理者,他就需要去帮助一线管理者了,这个超出我的经验了。我想说的是,管理也是技术,不比编程更难,但也不见得比编程简单。

除了管理能力外,保持大规模团队的研发效率,还需要规范和工具支撑。使用一致的基础设施(如版本控制、测试环境、发布流程、沟通协议),规范化大家的代码组织结构,抽取共有的技术服务,防止重复造轮子…… 这些都是非常具体、非常现实的技术问题。小公司几万行的代码做整体技术升级,找个牛逼的程序员就能搞定了,大公司几十万、几百万的代码做技术升级,没有任何一个英雄主义程序员能搞定,解决这类问题需要有前瞻性的架构,需要有善于沟通的架构师。虽然过程中难免会需要和人扯皮开会,但做好了也是极富有成就感的事情。

第三个问题,在大公司要面对更大的业务复杂度。也许是大公司产品经理太多了,大家都想折腾点东西出来,所以各种功能特性不停加不停变。这时候技术人员就不得不去理解各种业务的含义,我们都知道,如果实现和业务意义不吻合,最终的代码就会变成一个无人可维护的怪胎,因此优秀的技术人员就能很好识别业务边界,把小怪兽关在各自的笼子里,防止他们聚在一起搞得天翻地覆。再优秀的技术人员,就能说服产品经理,“这么干是不对的”。这方面我推荐《Domain Driven Design》。

大公司的普遍弊端

我并不是说任何一个大公司的程序员都会去面对上面三个问题,事实上刚入职的新人程序员一般只会处理很小业务范围内的没有太大挑战的任务。不过只要你有兴趣并持续提高自己能力,还是有很大机会去面对并处理这些问题的,因为公司的管理者终究是期望有人站出来帮他们排忧解难的。不过大公司或多或少都有一些普遍的问题。

首先是人浮于事、文山会海。有很多人不停开会、不停写邮件,就是不干活,搞的你也没好心情干活。吐槽归吐槽,我还是会仔细想想为什么这样。首先,沟通是必要的,两三个人干活打个招呼就行了,十多人干活就需要开会,事情多了,会也自然多,再加上很多人其实没有基本的主持会议技能,那很容易搞成垃圾会议。其次,公司大了自然会有一些兵油子,每句话说出来都大方得体,但就没见他把事情落地,更别提自己挽起袖子干了,比较麻烦的是这些人普遍层级还相对高点。我能做的,就是离他们远点。

其次是目光狭隘。如果一个程序员刚毕业就来到大公司,而且恰好这几年这家大公司业务和技术突飞猛进,那他的眼光就容易受限,觉得自己的公司全国甚至全世界最牛逼,再加上我们中国人普遍存在的报喜不报忧文化,他的这种盲目就更容易被环境所固化了。于是一不小心,一些人的技术视野就变得很窄,我在内部推广Git的时候,持疑惑最大的也就是一些工作年限较长的资深工程师。

还有,我个人比较头大的是,目标不一致。跨团队、跨部门沟通的时候,你很容易发现自己在鸡同鸭讲。可能团队A关心技术架构,团队B关心业务指标,然后你上升一层,在部门层面做个决策先做什么后做什么。一会你又发现,部门A关心业务指标、部门B关心基础建设,你又得上升一层,可那一层离你好远…… 所以你会发现很多大公司很多人做事很大程度上是在靠个人影响力,而不是正规的流程。如果我曾经做过些成功的事情,我平时对大家也比较热心,那我做一些事情的时候,一些人会把自己的目标暂时放一边,来帮你一把,这就是俗称的“刷脸”。

总结

总结之前,有一点我要额外提一下,大公司毕竟是藏龙卧虎的地方,各个领域都有比较资深的人存在,如果公司文化鼓励分享,那你就很容易找机会请人家喝杯咖啡,聊聊。

做任何选择,如果你不考虑失去什么,只考虑得到什么,那就是典型的幼稚。因此选择大公司还是小公司,你不仅得明白你期望收获什么,还得坦然面对要失去的东西。我说这么多,基本就是告诉你,有些东西你肯定会失去,还有一些东西,如果你努力,你可能会得到。

十大算法,让你轻松进阶高手

算法一:快速排序算法

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

算法步骤:

1 从数列中挑出一个元素,称为 “基准”(pivot),

2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

算法二:堆排序算法

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

堆排序的平均时间复杂度为Ο(nlogn) 。

算法步骤:

创建一个堆H[0..n-1]

把堆首(最大值)和堆尾互换

3. 把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置

4. 重复步骤2,直到堆的尺寸为1

算法三:归并排序

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

算法步骤:

1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置

3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

4. 重复步骤3直到某一指针达到序列尾

5. 将另一序列剩下的所有元素直接复制到合并序列尾

算法四:二分查找算法

二分查找算法是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜 素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组 为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。折半搜索每次把搜索区域减少一半,时间复杂度为Ο(logn) 。

算法五:BFPRT(线性查找算法)

BFPRT算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分 析,BFPRT可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂 度,五位算法作者做了精妙的处理。

算法步骤:

1. 将n个元素每5个一组,分成n/5(上界)组。

2. 取出每一组的中位数,任意排序方法,比如插入排序。

3. 递归的调用selection算法查找上一步中所有中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。

4. 用x来分割数组,设小于等于x的个数为k,大于x的个数即为n-k。

5. 若i==k,返回x;若i<k,在小于x的元素中递归查找第i小的元素;若i>k,在大于x的元素中递归查找第i-k小的元素。

终止条件:n=1时,返回的即是i小元素。

算法六:DFS(深度优先搜索)

深度优先搜索算法(Depth-First-Search),是搜索算法的一种。它沿着树的深度遍历树的节点,尽可能深的搜索树的分 支。当节点v 的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发 现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。DFS属于盲目搜索。

深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。一般用堆数据结构来辅助实现DFS算法。

深度优先遍历图算法步骤:

1. 访问顶点v;

2. 依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;

3. 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。

上述描述可能比较抽象,举个实例:

DFS 在访问图中某一起始顶点 v 后,由 v 出发,访问它的任一邻接顶点 w1;再从 w1 出发,访问与 w1邻 接但还没有访问过的顶点 w2;然后再从 w2 出发,进行类似的访问,… 如此进行下去,直至到达所有的邻接顶点都被访问过的顶点 u 为止。

接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。

算法七:BFS(广度优先搜索)

广度优先搜索算法(Breadth-First-Search),是一种图形搜索算法。简单的说,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止。BFS同样属于盲目搜索。一般用队列数据结构来辅助实现BFS算法。

算法步骤:

1. 首先将根节点放入队列中。

2. 从队列中取出第一个节点,并检验它是否为目标。

如果找到目标,则结束搜寻并回传结果。

否则将它所有尚未检验过的直接子节点加入队列中。

3. 若队列为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传“找不到目标”。

4. 重复步骤2。

算法八:Dijkstra算法

戴克斯特拉算法(Dijkstra’s algorithm)是由荷兰计算机科学家艾兹赫尔·戴克斯特拉提出。迪科斯彻算法使用了广度优先搜索解决非负权有向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

该算法的输入包含了一个有权重的有向图 G,以及G中的一个来源顶点 S。我们以 V 表示 G 中所有顶点的集合。每一个图中的边,都是两个顶点所形成的有序元素对。(u, v) 表示从顶点 u 到 v 有路径相连。我们以 E 表示G中所有边的集合,而边的权重则由权重函数 w: E → [0, ∞] 定义。因此,w(u, v) 就是从顶点 u 到顶点 v 的非负权重(weight)。边的权重可以想像成两个顶点之间的距离。任两点间路径的权重,就是该路径上所有边的权重总和。已知有 V 中有顶点 s 及 t,Dijkstra 算法可以找到 s 到 t的最低权重路径(例如,最短路径)。这个算法也可以在一个图中,找到从一个顶点 s 到任何其他顶点的最短路径。对于不含负权的有向图,Dijkstra算法是目前已知的最快的单源最短路径算法。

算法步骤:

1. 初始时令 S={V0},T={其余顶点},T中顶点对应的距离值

若存在<v0,vi>,d(V0,Vi)为<v0,vi>弧上的权值

若不存在<v0,vi>,d(V0,Vi)为∞

2. 从T中选取一个其距离值为最小的顶点W且不在S中,加入S

3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值

重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止

算法九:动态规划算法

动态规划(Dynamic programming)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。

动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多 子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个 子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。

关于动态规划最经典的问题当属背包问题。

算法步骤:

1. 最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。

2. 子问题重叠性质。子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。 动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是 在表格中简单地查看一下结果,从而获得较高的效率。

算法十:朴素贝叶斯分类算法

朴素贝叶斯分类算法是一种基于贝叶斯定理的简单概率分类算法。贝叶斯分类的基础是概率推理,就是在各种条件的存在不确定,仅知其出现概率的情况下, 如何完成推理和决策任务。概率推理是与确定性推理相对应的。而朴素贝叶斯分类器是基于独立假设的,即假设样本每个特征与其他特征都不相关。

朴素贝叶斯分类器依靠精确的自然概率模型,在有监督学习的样本集中能获取得非常好的分类效果。在许多实际应用中,朴素贝叶斯模型参数估计使用最大似然估计方法,换言之朴素贝叶斯模型能工作并没有用到贝叶斯概率或者任何贝叶斯模型。

 

foreach

(PHP 4, PHP 5)

foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。有两种语法:

foreach (array_expression as $value)
    statement
foreach (array_expression as $key => $value)
    statement

第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。

第二种格式做同样的事,只除了当前单元的键名也会在每次循环中被赋给变量 $key

还能够自定义遍历对象

Note:

foreach 开始执行时,数组内部的指针会自动指向第一个单元。这意味着不需要在 foreach 循环之前调用 reset()

由于 foreach 依赖内部数组指针,在循环中修改其值将可能导致意外的行为。

可以很容易地通过在 $value 之前加上 & 来修改数组的元素。此方法将以引用赋值而不是拷贝一个值。

<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // 最后取消掉引用
?>

$value 的引用仅在被遍历的数组可以被引用时才可用(例如是个变量)。以下代码则无法运行:

<?php
foreach (array(1, 2, 3, 4) as &$value) {
$value = $value * 2;
}

?>

Warning

数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。

Note:

foreach 不支持用“@”来抑制错误信息的能力。

用户可能注意到了以下的代码功能完全相同:

<?php
$arr = array("one", "two", "three");
reset($arr);
while (list(, $value) = each($arr)) {
echo "Value: $value<br>\n";
}

foreach ($arr as $value) {
echo "Value: $value<br />\n";
}
?>

以下代码功能也完全相同:

<?php
$arr = array("one", "two", "three");
reset($arr);
while (list($key, $value) = each($arr)) {
echo "Key: $key; Value: $value<br />\n";
}

foreach ($arr as $key => $value) {
echo "Key: $key; Value: $value<br />\n";
}
?>

示范用法的更多例子:

<?php
/* foreach example 1: value only */

$a = array(1, 2, 3, 17);

foreach ($a as $v) {
echo "Current value of \$a: $v.\n";
}

/* foreach example 2: value (with its manual access notation printed for illustration) */

$a = array(1, 2, 3, 17);

$i = 0; /* for illustrative purposes only */

foreach ($a as $v) {
echo "\$a[$i] => $v.\n";
$i++;
}

/* foreach example 3: key and value */

$a = array(
"one" => 1,
"two" => 2,
"three" => 3,
"seventeen" => 17
);

foreach ($a as $k => $v) {
echo "\$a[$k] => $v.\n";
}

/* foreach example 4: multi-dimensional arrays */
$a = array();
$a[0][0] = "a";
$a[0][1] = "b";
$a[1][0] = "y";
$a[1][1] = "z";

foreach ($a as $v1) {
foreach ($v1 as $v2) {
echo "$v2\n";
}
}

/* foreach example 5: dynamic arrays */

foreach (array(1, 2, 3, 4, 5) as $v) {
echo "$v\n";
}
?>

用 list() 给嵌套的数组解包

(PHP 5 >= 5.5.0)

PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中,只需将 list() 作为值提供。

例如:

<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a, $b)) {
// $a contains the first element of the nested array,
// and $b contains the second element.
echo "A: $a; B: $b\n";
}
?>

以上例程会输出:

A: 1; B: 2
A: 3; B: 4

list() 中的单元可以少于嵌套数组的,此时多出来的数组单元将被忽略:

<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a)) {
// Note that there is no $b here.
echo "$a\n";
}
?>

以上例程会输出:

1
3

如果 list() 中列出的单元多于嵌套数组则会发出一条消息级别的错误信息:

<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a, $b, $c)) {
echo "A: $a; B: $b; C: $c\n";
}
?>

以上例程会输出:

Notice: Undefined offset: 2 in example.php on line 7
A: 1; B: 2; C: 

Notice: Undefined offset: 2 in example.php on line 7
A: 3; B: 4; C: 
add a note add a note

User Contributed Notes 22 notes

119

adam dot sindelar at gmail dot com

7 years ago
You can also use the alternative syntax for the foreach cycle:

<?php
foreach($array as $element):
#do something
endforeach;
?>

Just thought it worth mentioning.

70

php at darkain dot com

2 years ago
"Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset()."

I cannot stress this point of the documentation enough! Here is a simple example of exactly why this must be done:

<?php
$arr1
= array("a" => 1, "b" => 2, "c" => 3);
$arr2 = array("x" => 4, "y" => 5, "z" => 6);

foreach ($arr1 as $key => &$val) {}
foreach (
$arr2 as $key => $val) {}

var_dump($arr1);
var_dump($arr2);
?>

The output is:
array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> &int(6) }
array(3) { ["x"]=> int(4) ["y"]=> int(5) ["z"]=> int(6) }

Notice how the last index in $arr1 is now the value from the last index in $arr2!

40

tedivm at tedivm dot com

6 years ago
foreach and the while/list/each methods are not completely identical, and there are occasions where one way is beneficial over the other.

<?php
$arr
= array(1,2,3,4,5,6,7,8,9);

foreach($arr as $key=>$value)
{
unset(
$arr[$key + 1]);
echo
$value . PHP_EOL;
}
?>
Output:
1 2 3 4 5 6 7 8 9

<?php
$arr
= array(1,2,3,4,5,6,7,8,9);

while (list($key, $value) = each($arr))
{
unset(
$arr[$key + 1]);
echo
$value . PHP_EOL;
}
?>
Output:
1 3 5 7 9

[EDIT BY danbrown AT php DOT net: Contains a typofix by (scissor AT phplabs DOT pl) on 30-JAN-2009.]

17

Alastair Hole

1 year ago
What happened to this note:
"Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it."

Is this no longer the case?
It seems only to remain in the Serbian documentation: http://php.net/manual/sr/control-structures.foreach.php

5

Fred

11 months ago
If you want to use the list for multidimension arrays, you can nest several lists:

<?php
$array
= [
[
1, 2, array(3, 4)],
[
3, 4, array(5, 6)],
];

foreach ($array as list($a, $b, list($c, $d))) {
echo
"A: $a; B: $b; C: $c; D: $d;<br>";
};
?>

Will output:
A: 1; B: 2; C: 3; D: 4;
A: 3; B: 4; C: 5; D: 6;

And:

<?php
$array
= [
[
1, 2, array(3, array(4, 5))],
[
3, 4, array(5, array(6, 7))],
];

foreach ($array as list($a, $b, list($c, list($d, $e)))) {
echo
"A: $a; B: $b; C: $c; D: $d; E: $e;<br>";
};
Will output:
A: 1; B: 2; C: 3; D: 4; E: 5;
A: 3; B: 4; C: 5; D: 6; E: 7;
?>

13

dtowell

3 years ago
References created by foreach hang around past their best-used-by date. For example, the following:

<?php
$a
= array('abe','ben','cam');
foreach (
$a as $k=>&$n)
$n = strtoupper($n);
foreach (
$a as $k=>$n) // notice NO reference here!
echo "$n\n";
print_r($a);
?>

will result in:

ABE
BEN
BEN
Array
(
[0] => ABE
[1] => BEN
[2] => BEN
)

8

Oleg englishman at bigmir dot net

5 years ago
For those who'd like to traverse an array including just added elements (within this very foreach), here's a workaround:

<?php
$values
= array(1 => 'a', 2 => 'b', 3 => 'c');
while (list(
$key, $value) = each($values)) {
echo
"$key => $value \r\n";
if (
$key == 3) {
$values[4] = 'd';
}
if (
$key == 4) {
$values[5] = 'e';
}
}
?>

the code above will output:

1 => a
2 => b
3 => c
4 => d
5 => e

2

nehuen

1 year ago
foreach by reference internally deleted and created a new reference in each iteration, so it is not possible to directly use this value as a variable parameter values​​, look at the following example where the problem is observed and a possible solution:

<?php
class test
{
private
$a = false;
private
$r = null;
public function
show(&$v)
{
if(!
$this->a)
{
$this->a = true;
$this->r = &$v;
}
var_dump($this->r);
}
public function
reset()
{
$this->a = false;
}
}

$t = new test();

$a = array(array(1,2),array(3,4),array(5,6));
foreach(
$a as &$p)
$t->show($p);

/* Output obtain:
array (size=2)
0 => int 1
1 => int 2
array (size=2)
0 => int 1
1 => int 2
array (size=2)
0 => int 1
1 => int 2
*/

$t->reset();
foreach(
$a as $p)
{
$b = &$p;
$t->show($b);
}

/* Output obtain:
array (size=2)
0 => int 1
1 => int 2
array (size=2)
0 => int 3
1 => int 4
array (size=2)
0 => int 5
1 => int 6
*/

5

nobody at nobody dot com

3 years ago
<?php
$d3
= array('a'=>array('b'=>'c'));
foreach(
$d3['a'] as &$v4){}
foreach(
$d3 as $v4){}
var_dump($d3);
?>
will get something look like this:
array(1) {
["a"]=>
array(1) {
["b"]=>
&array(1) {
["b"]=>
*RECURSION*
}
}
}
then you try to walk some data with this array.
the script run out of memory and connect reset by peer

the document says:
Warning
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

so what I learn is that NEVER ignore """Warning""" in document....

1

Anonymous

10 months ago
modifying array while foreach'ing it(yeah, such slime code;-)
if elements were added on last iteration or into array with 1 element, then added elements wont be iterated as foreach checks for pointer before iteration cycle
so it just quit and added elements wont be treated
1

pnc at balintx dot me

2 months ago
Just a simple strange behavior I have ran into:

If you accidentally put a semicolon after the foreach statement, you get no errors, but the loop will only run on the last element of the array:
<?php
$array
= array(1,2,3);
foreach (
$array as $key);
{
echo
$key;
}
// output: 3
?>

Correctly:
<?php
$array
= array(1,2,3);
foreach (
$array as $key)
{
echo
$key;
}
// output: 123
?>

It took me a while to find that semicolon.

1

billardmchl at aol dot com

4 years ago
This function find well the words, add well adds a () around short words, but the
array at the end of th function is the same as at the beginning.

<?php
function isole_mots($chaine)
{
$chaine = "le petit chat est fou";
$mots = preg_split('/[!,-.;?:()[ ]/', $chaine, -1, PREG_SPLIT_NO_EMPTY);
foreach (
$mots as $mot)
{
if (
strlen($mot) <= 3)
$mot = "(".$mot.")";
print
" inside foreach $mot <br>";
}
print
"after foreach array mots";
print_r($mots);
die();
return
$mots;
}
?>

inside foreach (le)
inside foreach petit
inside foreach chat
inside foreach (est)
inside foreach (fou)
after foreach array motsArray ( [0] => le [1] => petit [2] => chat [3] => est [4] => fou )

-1

Delian Krustev

2 years ago
I want to add some inline comments to dtowell's piece of code about the iteration by reference:

<?php

$a = array('abe','ben','cam');

foreach ($a as $k=>&$n)
$n = strtoupper($n);

# At the end of this cycle the variable $n refers to the same memory as $a[2]
# So when the second "foreach" assigns a value to $n :

foreach ($a as $k=>$n) // notice NO reference here!
echo "$n\n";

# it is also modifying $a[2] .
# So on the three repetitions of the second "foreach" the array will look like:
# 1. ('abe','ben','abe') - assigned the value of the first element to the last element
# 2. ('abe','ben','ben') - assigned the value of the second element to the last element
# 3. ('abe','ben','ben') - assigned the value of the third element to itself

print_r($a);
?>

-3

84td84 at gmail dot com

8 months ago
@tedivm at tedivm dot com

With foreach you can specify the & for $value. In this case you modify the original array. Otherwise you operate on a "copy" (not on the original array).
Consider the following example, that is identical with yours, except i use the & for $value:

$arr = array(1,2,3,4,5,6,7,8,9);
foreach($arr as $key=>&$value)
{
unset($arr[$key + 1]);
echo "\n\n--------\nKey: $key\nValue: $value\n";
var_dump($arr);
echo   "--------";
}
echo "\n\nARR at the end:\n";
var_dump($arr);

In foreach it prints the following values:
1, 3, 5, 7, 9

And you can see, that in the loop the corresponding elements are removed from arr.
So at the end you'll have an array, that contains only 1, 3, 5, 7, 9.

-5

Anteaus

10 months ago
"In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference. "

PLEASE CLARIFY! -Does this still hold true in php5.4+, where explicit passing by reference is seemingly forbidden- or is this now illegal syntax?

-8

admin at gadelkareem dot com

1 year ago
Not sure if this is considered a bug
<?php
$array
= ['numbers' => [1, 2, 3]];
foreach (
$array as &$numbers) {
$numbers = [4, 5, 6];
}
var_dump($array);
modify_array($array);
var_dump($array);

function modify_array($array_arg)
{
$array_arg['numbers'] = [1, 2, 3];
}

Prints

array(1) {
[
"numbers"]=>
&array(
3) {
[
0]=>
int(4)
[
1]=>
int(5)
[
2]=>
int(6)
}
}
array(
1) {
[
"numbers"]=>
&array(
3) {
[
0]=>
int(1)
[
1]=>
int(2)
[
2]=>
int(3)
}
}
?>
Notice that $array was not passed by reference to modify_array() however the nested array was modified. It sounds logical but not sure if this is documented!

-11

sainthyoga2003 at gmail dot com

1 year ago
Note that prior to PHP 5.5 you will get a T_LIST parse error for:
<?php
foreach($list as list($a, $b)) {...
?>
-4

liam666 at donnelly-house dot net

1 year ago
This is a decent, simple, and easy way to reference other values of an associative array when using foreach. (effective "next", "prev", etc.)
The only care that needs to be taken is if the array is HUGE in size, so you don't run into memory use problems. (and potential speed issues)

This example uses the 'primary' array, $aPublishSeq, which is ksort-ed to put the array in order according to the associative keys. The array is then copied using a foreach loop to make a duplicate array where the key and value order correspond to the first array, but the keys are sequential numeric starting at zero.

ksort ($aPublishSeq, SORT_STRING);     // put them all in the right order keeping array keys
foreach ($aPublishSeq as $aValue)
$aPublishIdx[] = $aValue;          // duplicate array using corresponding sequential numeric keys

Now, in the usage foreach loop, an index variable is used to keep in sync with the associative array.

$PubIdx = -1;     // start at -1 to start at 0 below

foreach ($aPublishSeq as $sKey => $sValue) {

++$PubIdx;     // index into $aPublishIdx array of corresponding element in $aPublishSeq array (for "next" element check, etc.)

echo $aPublishIdx[$PubIdx  - 1]     // previous array value
echo $aPublishIdx[$PubIdx]          // current array value
echo $aPublishIdx[$PubIdx  + 1]     // next array value

....

It's simple, but it works, and without much muss or fuss.

-26

Voitcus at wp dot pl

3 years ago
You can even iterate through "dynamic" arrays that do not physically exist, but are objects that implement Iterator interface. They don't need to be stored in memory when foreach starts.

Consider the array that contains some values (I called it $allValues in the example below) and we want to have only some of them (eg. the ones that are dividable by 2). I create an object that would serve as dynamic array, that means it would "dynamically update" its values together with $allValues. The main advantage is that I store only one array, and it's the only array I serialize.

An object of MyIter class will not contain any values itself:
<?php
class MyIter implements Iterator { // you can implement ArrayAccess and Countable interfaces too, this will make class MyIter behave like a "real" array
private $position = 0; // an internal position of the current element
// please note that $position has nothing common with $allValues!

private function getTable(){ // prepare a temporary "static" table of all objects in the class
global $allValues;
$result=array(); // temporary variable
foreach($allValues as $obj){
if(
$obj % 2 == 0) // check if the value is even
$result[]=$obj; // if yes, I want it
}
return
$result;
}

// the all below declared methods are public and belong to the Iterator interface
function rewind() { // a method to start iterating
$this->position = 0; // just move to the beginning
}

function current() { // retrieves the current element
$table=$this->getTable(); // let us prepare a table
return $table[$this->position]; // and return the current element
}

function key() { // retrieves the current element's key
return $this->position; // this is used by foreach(... as $key=>$value), not important here
}

function next() { // move to next element
++$this->position;
}

function valid() { // check if the current element is valid (ie. if it exists)
return array_key_exists($this->position, $this->getTable());
}
}
// end of class

// now prepare the array of 12 elements
$allValues=array(0,1,2,3,4,5,6,7,8,9,10,11);

//we would like to have a dynamic array of all even values
$iterator=new MyIter();

foreach($iterator as $value){
echo
$value."<br />";
}
?>
This will result in:
0
2
4
6
8
10

(You may also like to see what var_dump($iterator) produces).

Another great advantage is that you can modify the main table "on-the-fly" and it has its impact. Let us modify the last foreach loop:
<?php
// ...all above shall stay as it was
foreach($iterator as $value){
echo
$value."<br />";
if(
$value==6){
$allValues=array(2,3);
echo
"I modified source array!<br />";
}
}
?>
This produces now:
0
2
4
6
I modified source array!

However, if you feel it is rather a catastrophic disadvantage (maybe for example, it shows the values 0, 4, and 6 which were removed when we reached 6), and wish to have a "static" array that will iterate even in modified objects, just call getTable() in rewind() method and save it in temporary (private perhaps) field. In my example getTable() is called every iteration, and it calls another foreach through $allValues, which together might be time-consuming. Consider what you need.

-29

Ashus

2 years ago
If you wondered how to create a list of all possible combinations of variable amount of arrays (multiple foreach), you might use this:

<?php

$a[0] = array('a1','a2');
$a[1] = array('b1','b2','b3');
$a[2] = array('c1','c2');

function getAllCombinations($a,$i,$s)
{
foreach (
$a[$i] as $v)
{
if (!isset(
$a[$i+1]))
{
echo
$s.$v."\n";
} else {
getAllCombinations($a,$i+1,$s.$v);
}
}
return
$s;
}

echo getAllCombinations($a,0,'');

?>

the result:

a1b1c1
a1b1c2
a1b2c1
a1b2c2
a1b3c1
a1b3c2
a2b1c1
a2b1c2
a2b2c1
a2b2c2
a2b3c1
a2b3c2

-33

simplex

8 years ago
"As of PHP 5, you can easily modify array's elements by preceding $value with &. This will assign reference instead of copying the value."

There are cases where array_walk or array_map are inadequate (conditional required) or you're just too lazy to write a function and pass values to it for use with array_map...

My solution to foreach for php 4 and 5 to modify values of an array directly:

<?php

$testarr = array("a" => 1, "b" => 2, "c" => 3, "d" => 4);

$testarr_keys = array_keys($testarr);
$testarr_values = array_values($testarr);

for ($i = 0; $i <= count($testarr) - 1; $i++) {
$testarr[$testarr_keys[$i]] = $testarr_values[$i] * 2;
}

print_r($testarr);
?>

-53

php at electricsurfer dot com

12 years ago
[Ed Note:  You can also use array_keys() so that you don't have to have the $value_copy variable --alindeman at php.net]

I use the following to modify the original values of the array:

<?php
foreach ($array as $key=>$value_copy)
{
$value =& $array[$key];
// ...
$value = 'New Value';
}
?>