约瑟夫·坎贝尔在《千面英雄》中提炼出的”单一神话”(Monomyth)模式,是人类叙事的一个通用框架。从程序员的视角看,英雄之旅就是一个标准的函数调用链,每个阶段都是一个独立的函数,接收特定输入,产生预期输出,并为下一个函数准备参数。本章将英雄之旅解构为12个核心函数,探讨如何像编写模块化代码一样构建故事。
坎贝尔的英雄之旅可以映射为以下函数序列:
hero = ordinaryWorld() // 1. 平凡世界
quest = callToAdventure(hero) // 2. 冒险召唤
if (refusalOfCall(hero, quest)) { // 3. 拒绝召唤
hero = meetingMentor(hero) // 4. 遇见导师
}
hero = crossingThreshold(hero) // 5. 跨越门槛
trials = bellyOfWhale(hero) // 6. 试炼之路
hero = initiation(hero, trials) // 7. 最深洞穴
reward = ordeal(hero) // 8. 严峻考验
hero = seizeReward(hero, reward) // 9. 获得报酬
hero = roadBack(hero) // 10. 归途
hero = resurrection(hero) // 11. 复活重生
return apotheosis(hero) // 12. 带宝归来
函数签名:建立基准状态,返回未觉醒的主角
输入:世界设定、角色背景 输出:处于稳定状态的主角对象 副作用:建立读者的情感锚点
这个函数的核心任务是建立”正常”的基准线。就像单元测试中的setUp(),它创建了一个可对比的初始状态。《星球大战》中卢克在塔图因的农场生活,《黑客帝国》中尼奥的办公室日常,都是这个函数的经典实现。
关键在于”平凡”是相对的——对读者平凡,对角色也平凡。这创造了双重共鸣:读者理解这种日常,角色也安于这种日常。
函数签名:注入扰动,触发状态改变
输入:稳定状态的主角、外部事件 输出:包含使命信息的任务对象 副作用:打破平衡,创造张力
这是故事的interrupt信号。莱娅公主的全息信息、霍格沃茨的入学通知书、morpheus的红蓝药丸——都是对日常的中断请求。有效的召唤必须:
函数签名:可选的延迟函数,增加真实感
输入:主角、召唤 输出:布尔值(是否拒绝) 副作用:强化冲突,展示内心挣扎
这是一个条件分支。并非所有故事都包含这一步,但它的存在让角色更立体。拒绝的理由暴露了角色的:
《指环王》中弗罗多最初拒绝离开夏尔,正是因为这三者的结合。
函数签名:依赖注入,提供必要的工具和知识
输入:迷茫的主角 输出:装备了新能力的主角 副作用:建立成长路径
导师是一个依赖注入器,为主角提供:
甘道夫、邓布利多、莫斐斯——他们都是这个函数的实例。导师不解决问题,而是赋能主角自己解决问题。
函数签名:环境切换,正式进入冒险状态
输入:准备就绪的主角 输出:进入新世界的主角 副作用:不可逆转的承诺
这是git commit –no-verify的时刻——主角正式提交到冒险中。物理上可能是:
关键特征:这个跨越通常是单向的,至少在心理上不能简单撤销。
函数签名:循环结构,通过迭代提升能力
输入:初级英雄 输出:经验值增加的英雄 副作用:展示成长曲线
while (!hero.isReady()) {
challenge = generateChallenge()
result = hero.face(challenge)
hero.exp += calculateExp(result)
hero.levelUp()
}
这个阶段是典型的训练循环。每个小挑战都是一次迭代,累积经验值。《龙珠》的修炼、《火影》的任务、游戏中的支线任务,都在执行这个循环。
函数签名:准备阶段,配置最终对决
输入:成长后的英雄、积累的线索 输出:明确目标的英雄 副作用:聚焦冲突,提升紧张感
这是boss战前的准备函数。英雄已经:
《星球大战》中潜入死星,《指环王》中接近末日山脉,都是这个函数的调用。
函数签名:核心冲突解决,最大的挑战
输入:全力以赴的英雄 输出:转变后的英雄(可能包含”死亡”) 副作用:情感高潮,主题呈现
这是故事的性能瓶颈,所有线程都在这里汇聚。英雄面对:
关键是”象征性死亡”——旧的自我必须死去,新的自我才能诞生。
函数签名:收获阶段,获得任务奖励
输入:胜利的英雄 输出:持有宝物的英雄 副作用:暂时的满足感
报酬可以是:
这个函数常常包含twist——报酬可能不是预期的形式。
函数签名:返回过程,但已非原来的自己
输入:携带宝物的英雄 输出:正在返回的英雄 副作用:反思与整合
归途不是简单的reverse(),而是带着新身份重新审视来路。可能遭遇:
函数签名:最终测试,证明真正的转变
输入:接近终点的英雄 输出:完全转变的英雄 副作用:主题的最终确认
这是最后的单元测试——英雄必须证明自己真的改变了。通常需要:
《黑客帝国》中尼奥的复活,字面上实现了这个函数。
函数签名:完成循环,分享所得
输入:完成蜕变的英雄 输出:影响世界的英雄 副作用:新的平衡状态
英雄带回的不仅是宝物,更是transformation。他们成为:
循环完成,但世界已经改变。新的平凡世界建立,等待下一次召唤。
英雄之旅的12个函数并非都是必需的。就像函数的可选参数,某些步骤可以省略或使用默认值。
function heroJourney({
ordinary = true, // 是否展示平凡世界
refusal = false, // 是否包含拒绝召唤
mentor = null, // 导师角色(可为空)
trials = 3, // 试炼次数
death = 'symbolic', // 死亡类型:literal | symbolic | none
return = true // 是否返回原世界
}) { ... }
最简化的英雄之旅可能只包含:
这是许多短篇故事的结构。例如海明威的《老人与海》:
不同文化对某些参数有不同的默认值:
西方倾向:
东方倾向:
《西游记》虽然有外部导师(菩萨),但真正的成长来自内心(悟)。每一难都是外部考验,更是内心修炼。
某些参数组合形成了特定的故事类型:
悲剧模式:
{
refusal: false, // 毫不犹豫接受命运
mentor: false, // 没有指导
death: 'literal', // 真实死亡
return: false // 无法归来
}
成长模式:
{
ordinary: true, // 强调对比
refusal: true, // 初始抗拒
trials: many, // 大量试炼
death: 'symbolic', // 精神重生
return: true // 必须回归
}
史诗模式:
{
ordinary: false, // 直接开始
mentor: multiple, // 多个导师
trials: extensive, // 漫长试炼
death: 'multiple', // 多次重生
return: 'transformed' // 改变世界
}
同一个函数在不同语境下可以有不同的实现,这就是英雄之旅的多态性。
这个函数可以接受不同类型的参数:
类型1:外部召唤
callToAdventure(MessageType message)
// 霍格沃茨来信、莱娅公主求救
类型2:内部召唤
callToAdventure(DesireType desire)
// 复仇的渴望、证明自己的需要
类型3:意外召唤
callToAdventure(AccidentType accident)
// 意外获得超能力、偶然发现秘密
《蜘蛛侠》同时使用了多个重载:
导师函数可以有完全不同的实现方式:
传统导师
class WiseMentor implements Mentor {
teach() { return "explicit_knowledge" }
support() { return "emotional_guidance" }
}
// 甘道夫、邓布利多
对手导师
class AntagonistMentor implements Mentor {
teach() { return "learning_by_conflict" }
support() { return "growth_through_challenge" }
}
// 《龙珠》中的贝吉塔、《火影》中的佐助
集体导师
class CollectiveMentor implements Mentor {
teach() { return "distributed_wisdom" }
support() { return "community_support" }
}
// 《海贼王》的草帽团、《哈利波特》的DA军团
自我导师
class SelfMentor implements Mentor {
teach() { return "self_discovery" }
support() { return "inner_strength" }
}
// 《少年派》的派、《荒野求生》的主角
试炼函数根据故事类型有不同的实现:
战斗升级型
function combatTrials(hero) {
enemies = [weak, medium, strong]
for (enemy of enemies) {
hero.fight(enemy)
hero.powerLevel++
}
}
解谜进阶型
function puzzleTrials(hero) {
puzzles = [simple, complex, paradox]
for (puzzle of puzzles) {
hero.solve(puzzle)
hero.intelligence++
}
}
社交考验型
function socialTrials(hero) {
relationships = [trust, betrayal, love]
for (relationship of relationships) {
hero.navigate(relationship)
hero.empathy++
}
}
道德选择型
function moralTrials(hero) {
dilemmas = [minor, major, ultimate]
for (dilemma of dilemmas) {
hero.choose(dilemma)
hero.wisdom++
}
}
在函数式编程中,纯函数没有副作用,相同输入总是产生相同输出。但故事需要副作用——角色必须改变,世界必须受影响。
英雄之旅中的某些函数可以是纯函数:
纯函数示例
function calculateReward(heroLevel, challengeDifficulty) {
return heroLevel * challengeDifficulty * REWARD_MULTIPLIER
}
function determineOutcome(heroStrength, enemyStrength) {
return heroStrength > enemyStrength ? 'victory' : 'defeat'
}
这些函数:
故事的推进依赖副作用,关键是如何管理:
状态容器模式
class StoryState {
hero: Hero
world: World
relationships: Map
// 所有状态改变都通过方法进行
transformHero(transformation) {
this.hero = transformation(this.hero)
this.logChange('hero', transformation)
}
}
事件驱动模式
class StoryEvents {
emit(event, data) {
// 记录所有副作用
this.eventLog.push({event, data, timestamp})
// 触发相关变化
this.listeners[event].forEach(listener => listener(data))
}
}
某些副作用是故事的核心价值:
人物成长的副作用
世界变化的副作用
关系演变的副作用
使用Monad模式
class StoryMonad {
constructor(value) {
this.value = value
this.sideEffects = []
}
map(fn) {
const result = fn(this.value)
return new StoryMonad(result.value)
.addSideEffect(result.sideEffect)
}
addSideEffect(effect) {
this.sideEffects.push(effect)
return this
}
}
这样可以:
高阶函数接受函数作为参数或返回函数。在英雄之旅中,某些关键函数展现了高阶特性。
导师不是给予答案,而是给予获得答案的方法:
function createMentor(philosophy) {
return function(hero, challenge) {
switch(philosophy) {
case 'eastern':
return situation => hero.meditate(situation)
case 'western':
return situation => hero.analyze(situation)
case 'practical':
return situation => hero.experiment(situation)
}
}
}
// 使用
const yoda = createMentor('eastern')
const gandalf = createMentor('western')
const ironman = createMentor('practical')
// 英雄获得的不是答案,而是解决问题的方法
hero.problemSolver = yoda(hero, currentChallenge)
《星球大战》中,尤达教给卢克的不是具体招式,而是”Do or do not, there is no try”的哲学——一个思维框架函数。
门槛函数是一个高阶过滤器:
function createThreshold(requirements) {
return function(hero) {
const tests = [
hero => hero.courage > requirements.minCourage,
hero => hero.wisdom > requirements.minWisdom,
hero => hero.hasItem(requirements.keyItem),
hero => hero.hasCompleted(requirements.prerequisite)
]
return tests.every(test => test(hero))
}
}
// 不同的门槛
const enterMordor = createThreshold({
minCourage: 10,
keyItem: 'ring',
prerequisite: 'destroyIsengard'
})
const enterHogwarts = createThreshold({
minWisdom: 5,
keyItem: 'letter',
prerequisite: 'turn11'
})
转变是多个函数的组合:
function createTransformation(...stages) {
return function(hero) {
return stages.reduce((currentHero, stage) => {
return stage(currentHero)
}, hero)
}
}
// 复杂的转变过程
const becomeJedi = createTransformation(
hero => ({...hero, forceSensitive: true}),
hero => ({...hero, lightsaber: true}),
hero => ({...hero, training: 'complete'}),
hero => ({...hero, wisdom: hero.wisdom * 2}),
hero => ({...hero, alignment: 'light'})
)
const becomeSuperhero = createTransformation(
hero => ({...hero, powers: acquirePowers()}),
hero => ({...hero, identity: 'secret'}),
hero => ({...hero, responsibility: 'great'}),
hero => ({...hero, nemesis: createNemesis(hero)})
)
递归英雄
function recursiveHeroJourney(hero, depth = 0) {
if (depth > MAX_DEPTH) return hero
const newHero = miniJourney(hero)
if (newHero.hasAchievedGoal()) {
return newHero
}
return recursiveHeroJourney(newHero, depth + 1)
}
《盗梦空间》就是递归英雄之旅的完美体现——每一层梦境都是一个完整的英雄之旅。
迭代提升
function* iterativeGrowth(hero) {
while (true) {
hero = faceChallenge(hero)
hero = learnLesson(hero)
hero = incrementPower(hero)
yield hero
if (hero.isReady()) break
}
return hero
}
《龙珠》《海贼王》等长篇作品就是这种生成器模式——每个篇章yield一个更强的主角。
英雄之旅的函数式视角揭示了几个关键概念:
记住:英雄之旅不是rigid的模板,而是flexible的框架。像使用编程语言一样使用它——了解规则,然后创造性地打破规则。
关键公式:
练习2.1:识别《哈利·波特与魔法石》中的12个函数调用,标注哪些被省略或修改了。
练习2.2:将《三体》第一部的叶文洁故事线映射到英雄之旅函数,讨论哪些函数被”反向”了。
练习2.3:设计一个”非线性英雄之旅”,其中某些函数可以并行执行或乱序执行。画出可能的执行路径图。
练习2.4:创建一个高阶函数createGenreSpecificJourney(),它根据不同类型(爱情、侦探、恐怖)返回不同的英雄之旅实现。
练习2.5:分析《盗梦空间》的递归英雄之旅结构,标注每一层梦境中的英雄之旅元素。
练习2.6:设计一个”反英雄之旅”函数链,其中每个传统函数都被颠倒或腐化。举例说明如何应用到《教父》。
错误:强行让每个故事都包含全部12个步骤 正确:根据故事需要选择和调整函数
错误:严格按照1-12的顺序 正确:允许乱序、并行、循环
错误:每个函数只有一种标准实现 正确:探索多态性,创造独特实现
错误:只用坎贝尔的西方视角 正确:融合不同文化的英雄概念
错误:ordeal必须是外部敌人 正确:内心冲突同样有效,甚至更深刻
错误:必须有明确的导师角色 正确:导师可以是经历、书籍、甚至反面教材
错误:英雄必须回到起点 正确:有些旅程是单向的,回不去才是重点
错误:英雄只能变得更强大 正确:失去和软弱也是成长
记住:英雄之旅是工具,不是教条。像优秀的程序员对待设计模式一样对待它——理解原理,灵活应用,必要时大胆创新。