Scala破冰之旅

即使水墨丹青,何以绘出半妆佳人。
Scala是一门优雅而又复杂的程序设计语言,初学者很容易陷入细节而迷失方向。这也给我的写作带来了挑战,如果从基本的控制结构,再深入地介绍高级的语法结构,难免让人生厌。
为此,本文另辟蹊径,尝试通过一个简单有趣的例子,概括性地介绍Scala常见的语言特性。它犹如一个迷你版的Scala教程,带领大家一起领略Scala的风采。

问题的提出

有一名体育老师,在某次离下课还有五分钟时,决定玩一个游戏。此时有100名学生在上课,游戏的规则如下:

  1. 老师先说出三个不同的特殊数(都是个位数),比如3, 5, 7;让所有学生拍成一队,然后按顺序报数;
  2. 学生报数时,如果所报数字是「第一个特殊数(3)」的倍数,那么不能说该数字,而要说Fizz;如果所报数字是「第二个特殊数(5)」的倍数,要说Buzz;如果所报数字是「第三个特殊数(7)」的倍数,要说Whizz
  3. 学生报数时,如果所报数字同时是「两个特殊数」的倍数,也要特殊处理。例如,如果是「第一个(3)」和「第二个(5)」特殊数的倍数,那么也不能说该数字,而是要说FizzBuzz。以此类推,如果同时是三个特殊数的倍数,那么要说FizzBuzzWhizz
  4. 学生报数时,如果所报数字包含了「第一个特殊数」,那么也不能说该数字,而是要说Fizz。例如,要报13的同学应该说Fizz
  5. 如果数字中包含了「第一个特殊数」,需要忽略规则23,而使用规则4。例如要报35,它既包含3,同时也是5和7的倍数,要说Fizz,而不能说BuzzWhizz
  6. 否则,就要说对应的数字。

形式化

3, 5, 7为例,该问题可形式化地描述为:

r1: times(3) => Fizz ||
    times(5) => Buzz ||
    times(7) => Whizz
r2: times(3) && times(5) && times(7) => FizzBuzzWhizz ||
    times(3) && times(5) => FizzBuzz  ||
    times(3) && times(7) => FizzWhizz ||
    times(5) && times(7) => BuzzWhizz
r3: contains(3) => Fizz
rd: others => string of others
spec: r3 || r2 || r1 || rd

其中,times(3) => Fizz表示:当输入为3的倍数时,输出Fizz,其他以此类推。

建立测试环境

首先建立测试框架,建立反馈系统。这里使用scalatest的测试框架,它也是作者最喜欢的测试框架之一。

package scalaspec.fizzbuzz
import org.scalatest.Matchers._
import org.scalatest._
class RuleSpec extends FunSpec {
  describe("World") {
    it ("should not be work" ) {
      true should be(false)
    }
  }
}

运行测试用例,用于验证环境。

第一个测试用例

it ("times(3) -> fizz" ) {
  new Times(3, "Fizz").apply(3 * 2) should be("Fizz")
}

它先建立了一个规则:new Times(3, "Fizz"),表示如果是3的倍数,则输出Fizz。此时,如果输入数字3*2,断言预期的结果为Fizz

如果使用Java

为了快速通过测试,可以做简单实现。如果使用Java,Times实现大致如下。

public class Times(int n, String word) {
  private final int n;
  private final String word;
  public Times(int n, String word) {
    this.n = n;
    this.word = word;
  }
  public apply(int m) {
    return word;
  }
}

构造参数

从上述Java实现可以看出,当定义一个私有字段时,需要构造函数对它进行初始化。类似重复的「样板代码」在Scala中,可以在「主构造函数」中使用「构造参数」代替,彻底消除重复代码。

class Times(n: Int, word: String) {
  def apply(m: Int): String = word
}

类型的后缀修饰

Scala将类型的修饰放在后面,以便实现风格的「一致性」,包括:

  • 变量的类型修饰
  • 函数返回值的类型修饰
def apply(m: Int): String = word

类型推演

关于Scala类型推演,需要注意几点:

  • 函数原型后面不能略去=

apply函数原型后面的=不能略去,因为Scala将函数也看成普通的表达式。否则会限制函数的类型推演的能力,编译器将一律推演函数返回值类型为Unit(等价于Java中的void)。

  • 函数返回值常常略去return

例如,def apply(m: Int) = { return word }将产生编译错误,其需要明确地声明函数返回值的类型。所以,显式的return语句的使用得不偿失,除非return用于明确的提前中断。

  • 借助于类型推演的机制,变量、函数返回值的类型都可以略去;但是,当逻辑较为复杂时,代码的表达力将大打折扣
val i: Int = 0  // 类型修饰显得冗余

事实上,此处也可以略去apply方法的返回值的类型修饰,它依然能够推演为String类型。

def apply(m: Int) = word

apply方法

apply方法是一个特殊的方法,它可以简化方法调用的表现形式,使其行为更贴近函数的语义。在特殊的场景下,能够改善代码的表达力。

it ("times(3) -> fizz" ) {
  new Times(3, "Fizz").apply(3 * 2) should be("Fizz")
}

等价于:

it ("times(3) -> fizz" ) {
  new Times(3, "Fizz")(3 * 2) should be("Fizz")
}

实现Times

至此,测试通过了,但apply实现被写死了。因为Times的逻辑较为简单,可以快速实现它。

class Times(n: Int, word: String) {
  def apply(m: Int): String =
    if (m % n == 0) word else ""
}

万物皆是对象

Scala并没有像Java一样,针对「基本类型」(例如int),「数组类型」(例如int[])定义特殊的语法,它将世间万物都看成对象。
其中,m % n等价于m.%(n),而%只不过是Int的一个普通方法而已。

package scala
final abstract class Int private extends AnyVal {
  def %(x: Int): Int
  ...
}

注意,Int的实现由编译器完成,后续章节将讲述Int的修饰语法。

面向表达式

Scala是一门面向表达式的语言,它所有的程序结构都具有值,包括if-else,函数调用等。其中,if (m % n == 0) word else ""类似于Java中的三元表达式:m % n == 0 ? word : ""
因为两者功能重复,因此Scala并没有提供三元表达式的特性。

使用样本类

可以将Times设计为「样本类」。

case class Times(n: Int, word: String) {
  def apply(m: Int): String =
    if (m % n == 0) word else ""
}

当构造一个Times实例时,可以使用其「伴生对象」提供的工厂方法,从而略去new关键字,简化代码实现。

it ("times(3) -> fizz" ) {
  Times(3, "Fizz")(3 * 2) should be("Fizz")
}

揭秘样本类

使用case class定义的类称为「样本类」,它默认具有字段的Getter方法,并天然地拥有equals, hashCode等方法。
另外,「样本类」在其「伴生对象」中自动生成apply的工厂方法。当生产对象时,可以略去new关键字,使得语义更加简洁。
也就是说,case class Times...等价于:

class Times(val n: Int, val word: String) {
  def apply(m: Int): String =
    if (m % n == 0) word else ""
  override def equals(obj: Any): Boolean = ???
  override def hashCode(): Int = ???
  ...
}
object Times {
  def apply(n: Int, word: String) = new Times(n, word)
}

在本书中,除非特别说明,否则???表示函数实现的占位表示,仅仅为了方便举例。
其中,???定义在scala.Predef中;因为Predef的所有成员被编译器默认导入,它对所有程序公开。

def ??? = throw new NotImplementedError

伴生对象

object Times常常称为class Times的「伴生对象」。事实上,「伴生对象」中的方法,类似于Java中的static方法。但Scala摒弃了static的关键字,将面向对象的语义进行统一。
如果用Java设计case class Times,其实现类似于:

public class Times {
  private final int n;
  private final String word;
  public Times(int n, String word) {
    this.n = n;
    this.word = word;
  }
  public apply(int m) {
    return word;
  }
  // Getter方法
  public int n() {
    return n;
  }
  public String word() {
    return word;
  }
  // 自动生成equals, hashCode方法
  @Override
  public boolean equals(Object obj) {
    ...
  }
  @Override
  public int hashCode() {
    ...
  }
  // 静态工厂方法
  public static Times apply(int n, String word) {
    return new Times(n, word);
  }
  ...
}

实现Contains

有了Times实现的基础,可以很轻松地实现Contains的测试用例。

it ("contains(3) -> fizz" ) {
  Contains(3, "Fizz")(13) should be("Fizz")
}

依次类推,Contains可以快速实现为:

case class Contains(n: Int, word: String) {
  def apply(m: Int): String =
    if (m.toString.contains(n.toString)) word else ""
}

恭喜,测试通过了。

省略括号

m.toString等价于m.toString()。按照惯例,如果函数没有副作用,则可以略去小括号;相反,如果产生副作用,则显式地加上小括号用于警示。
如果函数定义时就没有使用小括号,用于表达函数无副作用;此时用户不能画蛇添足,添加多余的小括号;否则与函数定义的语义相驳了。

实现默认规则

对于默认规则,它只是简单地将输入的数字转变为字符串表示形式。

it ("default rule" ) {
  Default()(2) should be("2")
}

其中,Default可以快速实现为:

case class Default() {
  def apply(m: Int): String = m.toString
}

注意,case class Default(),及其调用点Default()(2),不能略去()

提取抽象

至此,发现Times, Contains, Default都具有相同的结构,可抽象出Rule的概念。

trait Rule {
  def apply(n: Int): String
}

特质的功效

此处使用trait定义了一个接口。事实上,trait不仅仅等价于Javainterface,它是Scala实现对象组合的重要机制。

实现特质

例如Times实现Rule特质,它使用extends Rule语法混入该「特质」。

case class Times(n: Int, word: String) extends Rule {
  def apply(m: Int): String =
    if (m % n == 0) word else ""
}

以此类推,Contains, Default实现方式相同,不再重述。

实现AnyOf

接下来,实现具有两个之间具有「逻辑与」关系的复合规则。先建立一个简单的测试用例:

it ("times(3) && times(5) -> FizzBuzz" ) {
  AllOf(Times(3, "Fizz"), Times(5, "Buzz"))(3*5) should be("FizzBuzz")
}

为了快速通过测试,可以先打桩实现。

case class AllOf(rules: Rule*) extends Rule {
  def apply(n: Int): String = "FizzBuzz"
}

变长参数

rules: Rule*表示变长的Rule列表,表示可以向AllOf的构造函数传递任意多的Rule实例。
事实上,rules: Rule*的真正类型为scala.collection.mutable.WrappedArray[Rule],所以rules: Rule*拥有普通集合类的一般特征,例如调用map, foreach, foldLeft等方法。

快速实现AllOf

case class AllOf(rules: Rule*) extends Rule {
  def apply(n: Int): String = {
    var result = StringBuilder.newBuilder
    rules.foreach { r =>
      result.append(r(n))
    }
    result.toString
  }
}

使用foldLeft

case class AllOf(rules: Rule*) extends Rule {
  def apply(n: Int): String =
    rules.foldLeft("") { r => _ + r.apply(n) }
}

因为r在函数字面值中仅过一次,可以使用占位符代替。

case class AllOf(rules: Rule*) extends Rule {
  def apply(n: Int): String =
    rules.foldLeft("") { _ + _.apply(n) }
}

因为apply方法具有特殊的函数调用语义,可以进一步简化实现。

case class AllOf(rules: Rule*) extends Rule {
  def apply(n: Int): String =
    rules.foldLeft("") { _ + _(n) }
}

实现AnyOf

接下来,实现具有两个之间具有「逻辑或」关系的复合规则。先建立一个简单的测试用例:

it ("times(3) -> Fizz || times(5) -> Buzz" ) {
  AnyOf(Times(3, "Fizz"), Times(5, "Buzz"))(3*5) should be("Fizz")
}

为了快速通过测试,可以先打桩实现。

case class AnyOf(rules: Rule*) extends Rule {
  def apply(n: Int): String = "Fizz"
}

快速实现AnyOf

case class AnyOf(rules: Rule*) extends Rule {
  def apply(n: Int): String =
    rules.map(_(n))
         .filterNot(_.isEmpty)
         .headOption
         .getOrElse("")
}

测试用例通过了。

提供工厂方法

因为Times, Contains, Default, AnyOf, AllOf都具有相同的句法结构,是一种典型的结构性重复设计,可以通过「工厂方法」消除它们之间的重复设计。
另外,为了简单函数调用的方式,可以使用Int => String的一元函数代替Rule特质。

重构测试用例

此时,可以定义一组新的测试用例集合,并使用describe分离用例组,并通过显示地导入所依赖的类型,与既有的用例集共存,互不干扰。
切忌删除既有的Rule特质,以及Times, Contains, Default, AllOf, AnyOf的实现,包括既有的测试用例;否则既有的测试用例失败,重构的安全网被撕破,将会让重构陷入一个极度危险的境界。

总之,重构应该保持小步快跑的基本原则。
按照TDD的规则,可以小步地,安全地逐一驱动实现各个工厂方法。

class RuleSpec extends FunSpec {
  ...
  describe("Rule using factory method") {
    import Rule._
    it ("times(3) -> fizz" ) {
      times(3, "Fizz")(3 * 2) should be("Fizz")
    }
  }
}

实现工厂

times的工厂方法也较容易实现,可以通过搬迁Times的逻辑至此即可。

object Rule {
  def times(n: Int, word: String): Int => String =
    m => if (m % n == 0) word else ""
}

至此,times实现通过测试。

小步快跑

以此类推,通过小步地TDD的微循环,将其他工厂方法驱动实现出来。

class RuleSpec extends FunSpec {
  ...

  describe("Rule using factory method") {
    import Rule._

    it ("times(3) -> fizz" ) {
      times(3, "Fizz")(3 * 2) should be("Fizz")
    }

    it ("contains(3) -> fizz" ) {
      contains(3, "Fizz")(13) should be("Fizz")
    }

    it ("default rule" ) {
      default(2) should be("2")
    }

    it ("times(3) && times(5) -> FizzBuzz" ) {
      anyof(times(3, "Fizz"), times(5, "Buzz"))(3*5) should be("FizzBuzz")
    }

    it ("times(3) -> Fizz || times(5) -> Buzz" ) {
      anyof(times(3, "Fizz"), times(5, "Buzz"))(3*5) should be("Fizz")
    }
  }
}

Rule伴生对象中的工厂方法实现如下。

object Rule {
  def times(n: Int, word: String): Int => String =
    m => if (m % n == 0) word else ""

  def contains(n: Int, word: String): Int => String =
    m => if (m.toString.contains(n.toString)) word else ""

  def default: Int => String =
    m => m.toString

  def anyof(rules: (Int => String)*): Int => String =
    m => rules.foldLeft("") { _ + _(m) }

  def allof(rules: (Int => String)*): Int => String =
    m => rules.map(_(m))
      .filterNot(_.isEmpty)
      .headOption
      .getOrElse("")
}

恭喜,通过所有测试。此时可以安全地删除Times, Contains, Default, AnyOf, AllOf,Rule特质,以及相关的遗留的测试用例了。

类型别名

可以对Int => String定义「类型别名」,消除类型的重复定义。

object Rule {
  type Rule = Int => String

  def times(n: Int, word: String): Rule =
    m => if (m % n == 0) word else ""

  def contains(n: Int, word: String): Rule =
    m => if (m.toString.contains(n.toString)) word else ""

  def default: Rule =
    m => m.toString

  def anyof(rules: Rule*): Rule =
    m => rules.foldLeft("") { _ + _(m) }

  def allof(rules: Rule*): Rule =
    m => rules.map(_(m))
      .filterNot(_.isEmpty)
      .headOption
      .getOrElse("")
}

至此,设计已经较为干净了。但发现times, contains, default之间存在微妙的重复结构。它们各自拥有隐晦的「匹配规则」,当匹配成功时,执行相应的「转换规则」。
其中,default的匹配规则、转换规则都比较特殊;因为它总是匹配成功,转换时简单地讲数字转换为字符串表示的形式。。

提取匹配器

先提取抽象的「匹配器」概念:Matcher。事实上,Matcher是一个「一元函数」,入参为Int,返回值为Boolean,是一种典型的「谓词」。
OO的角度看,always是一个典型的Null Object实现模式。

object Matcher {
  type Matcher = Int => Boolean
  def times(n: Int): Matcher = _ % n == 0
  def contains(n: Int): Matcher = _.toString.contains(n.toString)
  def always(bool: Boolean): Matcher = _ => bool
}

执行器:Action

然后再提取抽象的「执行器」概念:Action。事实上,Action也是一个「一元函数」,入参为Int,返回值为String。其本质类似于map操作,将定义域映射到值域。
OO的角度看,nop也是一个典型的Null Object实现模式。

object Action {
  type Action = Int => String
  def to(str: String): Action = _ => str
  def nop: Action = _.toString
}

原子操作

至此,可以提取times, contains, default三者之间的共公抽象:atom

def atom(matcher: => Matcher, action: => Action): Rule =
  m => if (matcher(m)) action(m) else ""

它表示的语义为:给定一个整数m,如果与Matcher匹配成功,则执行Action转换;否则返回空字符串。

新建一组用例集合

此时,新建一组用例集合,并使用atom的原子接口,并使用describe隔离新老用例集,显式地import所依赖的类型,保证既有测试用例可用。
Rule.atom, Matcher, Action可运行之前,切忌删除Rule中既有的times, contains, default,及其相应的测试用例。

class RuleSpec extends FunSpec {
  ...
  describe("using atom rule") {
    import Rule.{anyof, anyof, atom}
    import Matcher._
    import Action._
    val r1_3 = atom(times(3), to("Fizz"))
    val r1_5 = atom(times(5), to("Buzz"))
    it ("times(3) -> fizz" ) {
      r1_3(3 * 2) should be("Fizz")
    }
    val r3 = atom(contains(3), to("Fizz"))
    it ("contains(3) -> fizz" ) {
      r3(13) should be("Fizz")
    }
    val rd = atom(always(true), nop)
    it ("default rule" ) {
      rd(2) should be("2")
    }
    it ("times(3) && times(5) -> FizzBuzz" ) {
      anyof(r1_3, r1_5)(3*5) should be("FizzBuzz")
    }
    it ("times(3) -> Fizz || times(5) -> Buzz" ) {
      anyof(r1_3, r1_5)(3*5) should be("Fizz")
    }
  }
}

测试用例通过。

规则库

Composition Everywhere
此时,可以安全地删除Ruletimes, contains, default的实现,及其遗留的测试用例集。Rule最终实现为:

object Rule {
  type Rule = Int => String
  import Matcher.Matcher
  import Action.Action
  def atom(matcher: => Matcher, action: => Action): Rule =
    n => if (matcher(n)) action(n) else ""
  def anyof(rules: Rule*): Rule =
    n => rules.map(_(n))
      .filterNot(_.isEmpty)
      .headOption
      .getOrElse("")
  def allof(rules: Rule*): Rule =
    n => rules.foldLeft("") { _ + _(n) }
}

RuleFizzBuzzWhizz最核心的抽象,也是设计的灵魂所在。从语义上Rule分为2种基本类型,并且两者之间形成了隐式的「树型」结构,体现了「组合式设计」的强大威力。

  • 原子规则:atom
  • 复合规则: anyof, anyof

Rule也是一个「一元函数」,入参为Int,返回值为String。其中,def atom(matcher: => Matcher, action: => Action)的入参使用by-name的「惰性求值」特性。

完备用例集

针对于FizzBuzzWhizz问题,以3, 5, 7为例,其完备的用例集可以如下描述。此处使用表格驱动的方式组织用例,消除大量的重复代码,并改善其表达力。

import org.scalatest._
import prop._

class RuleSpec extends PropSpec with TableDrivenPropertyChecks with Matchers {
  import Rule._
  import Matcher._
  import Action._
  val spec = {
    val r1_3 = atom(times(3), to("Fizz"))
    val r1_5 = atom(times(5), to("Buzz"))
    val r1_7 = atom(times(7), to("Whizz"))
    val r1 = anyof(r1_3, r1_5, r1_7)
    val r2 = anyof(
      allof(r1_3, r1_5, r1_7),
      allof(r1_3, r1_5),
      allof(r1_3, r1_7),
      allof(r1_5, r1_7))
    val r3 = atom(contains(3), to("Fizz"))
    val rd = atom(always(true), nop);
    anyof(r3, r2, r1, rd)
  }
  val specs = Table(
    ("n",         "expect"),
    (3,           "Fizz"),
    (5,           "Buzz"),
    (7,           "Whizz"),
    (3 * 5,       "FizzBuzz"),
    (3 * 7,       "FizzWhizz"),
    ((5 * 7) * 2, "BuzzWhizz"),
    (3 * 5 * 7,   "FizzBuzzWhizz"),
    (13,          "Fizz"),
    (35/*5*7*/,   "Fizz"),
    (2,           "2")
  )
  property("fizz buzz whizz") {
    forAll(specs) { spec(_) should be (_) }
  }
}

语义模型

归纳上述设计,可以得到FizzBuzzWhizz的语义模型。

Rule:    Int => String
Matcher: Int => Boolean
Action:  Int => String

其中,Rule存在三种基本的类型:

Rule: atom | allof | anyof

三者之间构成了隐式的「树型结构」。

atom: (Matcher, Action) => String
allof: rule1 && rule2 ...
anyof: rule1 || rule2 ...

总结

本文通过对FizzBuzzWhizz的小游戏的设计和实现,首先尝试使用Scala的面向对象实现,然后采用函数式的设计;采用TDD的方式,演进式地完成功能的实现。

中间也曾遇到了「样本类」,「类型别名」,「伴生对象」等常用的技术。相信经过本文的实践,你应该对Scala有了一个大体的影响和感觉,接下来让我们开启Scala的愉快之旅吧。



本文作者:刘光聪(简书作者)

原文链接:http://www.jianshu.com/p/beb83c98a358
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

时间: 2024-09-20 06:53:06

Scala破冰之旅的相关文章

《大话Oracle Grid:云时代的RAC》——第1章 破冰之旅

第1章 破冰之旅 大话Oracle Grid:云时代的RAC 本书是专门为Oracle Grid 11.2(或者11.2 RAC)准备的. 说明: Oracle Grid Infrastructure.Oracle Clusterware是同一个软件的不同时期的名称,而Oracle RAC是这个软件支撑的解决方案,所以本书中我会混用这三个单词,但都是指的一个东西.另外,Oracle Grid Infrastructure这个名字有点太口 罗 嗦,所以我会把它简化成Grid或者GI,就是这样. 说

百度手机浏览器“破冰之旅”

7月9日,北京室外温度高达35摄氏度.就在这样炎热的午后,东直门来福士广场上却人头攒动.一大波人顶着烈日,高举手机,似乎是在拍什么,脸上貌似还带着一丝清凉的惬意.路过的行人不明所以,纷纷围观过来.不一会儿,围观的人群也都将手机掏了出来准备拍照.这些人究竟在拍什么呢? (东直门来福士广场围观"扫码"的人群) 原来这些人是在拍广场中央一个硕大的二维码.与普通二维码不同,这个二维码镶嵌在一面巨大的"冰墙"里,"冰墙"散发出阵阵凉意,形成了一个天然的&q

Scala开启之旅

嘿嘿,公司最近需要测试SPARK性能,赶上了.. 那LUA之后,SCALA也简单看看,,, 其实,我三月时买了本129元的SPARK的书,,全国只那一本哈. package com.hengheng.scala class HelloScala { } object HelloScala { def hello(name: String) : String = { "Hello " + name } def helloscala() { println("Hello Scal

破冰之旅:中兴受裁事件迎来曙光

中兴事件一波三折的剧情冲突,像极了一处折子戏.随着中国经济的全面崛起,中国无法避免与美国的正面冲突,类似中兴此次的事件会越来越多.这次中兴事件无疑给国际上,尤其... 近日,美国商务部于本月初以违反美国出口管制法规为例,对中兴采取限制出口措施.其间缘由,至今扑朔迷离,关于该事件的真伪性,国民对此更是纷纷不平.外交部长王毅在公交场合对此更是明确回应:"中美作为两个大国,既有合作,也有摩擦,这可能是一个常态.美国对中国企业进行贸易限制,这不是一个处理经贸矛盾的正确做法,损人且不利己. 关于该事件,毋

YY公司市值接近7亿美元 开启海外上市“破冰”之旅

自年初唯品会"流血"上市后,中概股在美国资本市场上一片沉寂,上市之路貌似被堵死.不过,这块坚冰近期又有松动迹象. 昨天,YY美国上市又有新进展,其向美国证券交易委员会(SEC)提交的文件显示,IPO发行价区间为10.5到12.5美元,拟发行780万股ADS,融资额介于8190万和9750万美元之间,据此推算,YY公司市值接近7亿美元. YY凭啥融资近亿美元 中国互联网企业已经有多久没从美国融到资金了?自从电商网站唯品会3月23日登陆纽交所,融资7264万美元之后,大半年过去,没有一家中

嘀嘀打车的破冰之旅 程维:创业不怕犯错

一家创业公司如何利用移动互联网撬动一个封闭保守的行业?创业者从嘀嘀打车的创业经历里能学到什么?1. 对创业项目的判断只有二三十分的可行性,觉得是机会,就赶紧做,创业不怕犯错.如果是百分之百可行,你已经没有机会了.2. 宁可慢一点,建立起靠谱的团队后再去做,磨刀不误砍柴工.3. 过去长期积累的经验并非是最重要的,甚至是可能妨碍发现新机会的.而一个创业者最需要具备的素质是:快速学习的能力.4. 如果你只是被动地接受外界的信息,做产品决策是不靠谱的,必须主动去调研,发现并解决问题.程维在嘀嘀打车201

打造第1品牌 天地超云的云计算破冰之旅

说到近几年的国内X86服务器市场,同质化依然是一个不可回避的问题.即便是像IBM.惠普和戴尔这样的国际巨头,也摆脱不了X86工业标准的要求,因此在谈到特色和个性化的时候都略显不足.而国内厂商这样的趋势则更为严重,难以给人留下深刻的印象.2010年5月,以韦轶群先生为领导成立了天地超云公司,定位在云计算应用领域,通过定制化的特色产品打开市场,为同质化严重的X86增添了新活力.弹指一挥间,超云公司成立已近3年,目前它的主打方向在何处?如何体现出个性化与差异化?如何实现"中国云计算第一品牌"

美国不认可中概股 YY不是破冰是敢死队

11月21日,主营语言聊天软件的网络技术公司YY成功在美国上市.由于今年行情低迷,YY的成功上市被业界称为破冰之旅.在上周六的创业邦年会上,记者专访了YY创始人.CEO李学凌.他向记者强调,YY不是破冰者,是敢死队.别人不敢去上市是怕估值被腰斩,YY不怕. 商报:YY上市时美国环境怎么样? 李学凌:这次YY上市遇到了很多环境方面的问题.一方面,此番上市之旅第一趟去美国就遇到了Sandy飓风,很多电话都打不通:另一方面也正好赶上美国大选,大家都关心大选去了,没人关心准备上市的公司. 商报:YY上市

《幸福来敲门》剩女夺目破冰家庭剧女性市场

<幸福来敲门>海报 新浪娱乐讯 由天视卫星传媒股份有限公司和北京同乐电影制作有限公司共同出品的电视剧<幸福来敲门>获得了开播以来一再稳健上升的收视好成绩,第六集的收视率已经近4%.<幸福来敲门>里蒋雯丽的精湛表演引发了女性观众对婚姻及两代人"剩女观"的共鸣.精巧剧情设置打破了家庭伦理剧"女性一边倒"现象,堪称家庭情感剧的"破冰之旅".无数的男性观众也将手中的遥控器交给了这部与"幸福"有关的家