如何实现Laravel的服务容器

栏目: 编程语言 · PHP · 发布时间: 6年前

内容简介:为了方便维护,我们把上面的数组封装到类里面。现在我们在绑定一个对象服务的时候,就必须要先把类实例化,如果绑定的服务没有被用到,那么类就会白白实例化,造成性能浪费。

如何实现服务容器(Ioc Container)

1. 容器的本质

  • 服务容器本身就是一个数组,键名就是服务名,值就是服务。
  • 服务可以是一个原始值,也可以是一个对象,可以说是任意数据。
  • 服务名可以是自定义名,也可以是对象的类名,也可以是接口名。
// 服务容器
$container = [
    // 原始值
    'text' => '这是一个字符串',
    // 自定义服务名
    'customName' => new StdClass(),
    // 使用类名作为服务名
    'StdClass' => new StdClass(),
    // 使用接口名作为服务名
    'Namespace\\StdClassInterface' => new StdClass(),
];

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

// 绑定服务到容器
$container['standard'] = new StdClass();
// 获取服务
$standard = $container['standard'];
var_dump($standard);

2. 封装成类

为了方便维护,我们把上面的数组封装到类里面。

$instances 还是上面的容器数组。我们增加两个方法, instance 用来绑定服务, get 用来从容器中获取服务。

class BaseContainer
{

    // 已绑定的服务
    protected $instances = [];

    // 绑定服务
    public function instance($name, $instance)
    {
        $this->instances[$name] = $instance;
    }

    // 获取服务
    public function get($name)
    {
        return isset($this->instances[$name]) ? $this->instances[$name] : null;
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

$container = new BaseContainer();
// 绑定服务
$container->instance('StdClass', new StdClass());
// 获取服务
$stdClass = $container->get('StdClass');
var_dump($stdClass);

3. 按需实例化

现在我们在绑定一个对象服务的时候,就必须要先把类实例化,如果绑定的服务没有被用到,那么类就会白白实例化,造成性能浪费。

为了解决这个问题,我们增加一个 bind 函数,它支持绑定一个回调函数,在回调函数中实例化类。这样一来,我们只有在使用服务时,才回调这个函数,这样就实现了按需实例化。

这时候,我们获取服务时,就不只是从数组中拿到服务并返回了,还需要判断如果是回调函数,就要执行回调函数。所以我们把 get 方法的名字改成 make 。意思就是生产一个服务,这个服务可以是已绑定的服务,也可以是已绑定的回调函数,也可以是一个类名,如果是类名,我们就直接实例化该类并返回。

然后,我们增加一个新数组 $bindings ,用来存储绑定的回调函数。然后我们把 bind 方法改一下,判断下 $instance 如果是一个回调函数,就放到 $bindings 数组,否则就用 make 方法实例化类。

class DeferContainer extend BaseContainer
{
    // 已绑定的回调函数
    protected $bindings = [];

    // 绑定服务
    public function bind($name, $instance)
    {
        if ($instance instanceof Closure) {
            // 如果$instance是一个回调函数,就绑定到bindings。
            $this->bindings[$name] = $instance;
        } else {
            // 调用make方法,创建实例
            $this->instances[$name] = $this->make($name);
        }
    }

    // 获取服务
    public function make($name)
    {
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }

        if (isset($this->bindings[$name])) {
            // 执行回调函数并返回
            $instance = call_user_func($this->bindings[$name]);
        } else {
            // 还没有绑定到容器中,直接new.
            $instance = new $name();
        }

        return $instance;
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

$container = new DeferContainer();
// 绑定服务
$container->bind('StdClass', function () {
    echo "我被执行了\n";
    return new StdClass();
});
// 获取服务
$stdClass = $container->make('StdClass');
var_dump($stdClass);

StdClass 这个服务绑定的是一个回调函数,在回调函数中才会真正的实例化类。如果没有用到这个服务,那回调函数就不会被执行,类也不会被实例化。

4. 单例

从上面的代码中可以看出,每次调用 make 方法时,都会执行一次回调函数,并返回一个新的类实例。但是在某些情况下,我们希望这个实例是一个单例,无论 make 多少次,只实例化一次。

这时候,我们给 bind 方法增加第三个参数 $shared ,用来标记是否是单例,默认不是单例。然后把回调函数和这个标记都存到 $bindings 数组里。

为了方便绑定单例服务,再增加一个新的方法 singleton ,它直接调用 bind ,并且 $shared 参数强制为 true

对于 make 方法,我们也要做修改。在执行 $bindings 里的回调函数以后,做一个判断,如果之前绑定时标记的 sharedtrue ,就把回调函数返回的结果存储到 $instances 里。由于我们是先从 $instances 里找服务,所以这样下次再 make 的时候就会直接返回,而不会再次执行回调函数。这样就实现了单例的绑定。

class SingletonContainer extends DeferContainer
{
    // 绑定服务
    public function bind($name, $instance, $shared = false)
    {
        if ($instance instanceof Closure) {
            // 如果$instance是一个回调函数,就绑定到bindings。
            $this->bindings[$name] = [
                'callback' => $instance,
                // 标记是否单例
                'shared' => $shared
            ];
        } else {
            // 调用make方法,创建实例
            $this->instances[$name] = $this->make($name);
        }
    }

    // 绑定一个单例
    public function singleton($name, $instance)
    {
        $this->bind($name, $instance, true);
    }

    // 获取服务
    public function make($name)
    {
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }

        if (isset($this->bindings[$name])) {
            // 执行回调函数并返回
            $instance = call_user_func($this->bindings[$name]['callback']);

            if ($this->bindings[$name]['shared']) {
                // 标记为单例时,存储到服务中
                $this->instances[$name] = $instance;
            }
        } else {
            // 还没有绑定到容器中,直接new.
            $instance = new $name();
        }

        return $instance;
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

$container = new SingletonContainer();
// 绑定服务
$container->singleton('anonymous', function () {
    return new class
    {
        public function __construct()
        {
            echo "我被实例化了\n";
        }
    };
});
// 无论make多少次,只会实例化一次
$container->make('anonymous');
$container->make('anonymous');
// 获取服务
$anonymous = $container->make('anonymous');
var_dump($anonymous)

上面的代码用 singleton 绑定了一个名为 anonymous 的服务,回调函数里返回了一个匿名类的实例。这个匿名类在被实例化时会输出一段文字。无论我们 make 多少次 anonymous ,这个回调函数只会被执行一次,匿名类也只会被实例化一次。

5. 自动注入

自动注入是Ioc容器的核心,没有自动注入就无法做到控制反转。

自动注入就是指,在实例化一个类时,用反射类来获取 __construct 所需要的参数,然后根据参数的类型,从容器中找到已绑定的服务。我们只要有了 __construct 方法所需的所有参数,就能自动实例化该类,实现自动注入。

现在,我们增加一个 build 方法,它只接收一个参数,就是类名。 build 方法会用反射类来获取 __construct 方法所需要的参数,然后返回实例化结果。

另外一点就是,我们之前在调用 make 方法时,如果传的是一个未绑定的类,我们直接new了这个类。现在我们把未绑定的类交给 build 方法来构建,因为它支持自动注入。

class InjectionContainer extends SingletonContainer
{

    // 获取服务
    public function make($name)
    {
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }
        if (isset($this->bindings[$name])) {
            // 执行回调函数并返回
            $instance = call_user_func($this->bindings[$name]['callback']);

            if ($this->bindings[$name]['shared']) {
                // 标记为单例时,存储到服务中
                $this->instances[$name] = $instance;
            }
        } else {
            // 使用build方法构建此类
            $instance = $this->build($name);
        }

        return $instance;
    }

    // 构建一个类,并自动注入服务
    public function build($class)
    {

        $reflector = new ReflectionClass($class);

        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            // 没有构造函数,直接new
            return new $class();
        }

        $dependencies = [];

        // 获取构造函数所需的参数
        foreach ($constructor->getParameters() as $dependency) {
            if (is_null($dependency->getClass())) {
                // 参数类型不是类时,无法从容器中获取依赖
                if ($dependency->isDefaultValueAvailable()) {
                    // 查找参数的默认值,如果有就使用默认值
                    $dependencies[] = $dependency->getDefaultValue();
                } else {
                    // 无法提供类所依赖的参数
                    throw new Exception('找不到依赖参数:' . $dependency->getName());
                }
            } else {
                // 参数类型是类时,就用make方法构建该类
                $dependencies[] = $this->make($dependency->getClass()->name);
            }
        }

        return $reflector->newInstanceArgs($dependencies);
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

class Redis
{
}

class Cache
{
    protected $redis;

    // 构造函数中依赖 Redis 服务
    public function __construct(Redis $redis)
    {
        $this->redis = $redis;
    }
}

$container = new InjectionContainer();
// 绑定Redis服务
$container->singleton(Redis::class, function () {
    return new Redis();
});
// 构建Cache类
$cache = $container->make(Cache::class);
var_dump($cache);

6. 自定义依赖参数

现在有个问题,如果类依赖的参数不是类或接口,只是一个普通变量,这时候就无法从容器中获取依赖参数了,也就无法实例化类了。

那么接下来我们就支持一个新功能,在调用 make 方法时,支持传第二个参数 $parameters ,这是一个数组,无法从容器中获取的依赖,就从这个数组中找。

当然, make 方法是用不到这个参数的,因为它不负责实例化类,它直接传给 build 方法。在 build 方法寻找依赖的参数时,就先从 $parameters 中找。这样就实现了自定义依赖参数。

需要注意的一点是, build 方法是按照参数的名字来找依赖的,所以 parameters 中的键名也必须跟 __construct 中参数名一致。

class ParametersContainer extends InjectionContainer
{
    // 获取服务
    public function make($name, array $parameters = [])
    {
        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }
        if (isset($this->bindings[$name])) {
            // 执行回调函数并返回
            $instance = call_user_func($this->bindings[$name]['callback']);

            if ($this->bindings[$name]['shared']) {
                // 标记为单例时,存储到服务中
                $this->instances[$name] = $instance;
            }
        } else {
            // 使用build方法构建此类
            $instance = $this->build($name, $parameters);
        }

        return $instance;
    }

    // 构建一个类,并自动注入服务
    public function build($class, array $parameters = [])
    {
        $reflector = new ReflectionClass($class);

        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            // 没有构造函数,直接new
            return new $class();
        }

        $dependencies = [];

        // 获取构造函数所需的参数
        foreach ($constructor->getParameters() as $dependency) {

            if (isset($parameters[$dependency->getName()])) {
                // 先从自定义参数中查找
                $dependencies[] = $parameters[$dependency->getName()];
                continue;
            }

            if (is_null($dependency->getClass())) {
                // 参数类型不是类或接口时,无法从容器中获取依赖
                if ($dependency->isDefaultValueAvailable()) {
                    // 查找默认值,如果有就使用默认值
                    $dependencies[] = $dependency->getDefaultValue();
                } else {
                    // 无法提供类所依赖的参数
                    throw new Exception('找不到依赖参数:' . $dependency->getName());
                }
            } else {
                // 参数类型是类时,就用make方法构建该类
                $dependencies[] = $this->make($dependency->getClass()->name);
            }
        }

        return $reflector->newInstanceArgs($dependencies);
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

class Redis
{
}

class Cache
{
    protected $redis;

    protected $name;

    protected $default;

    // 构造函数中依赖Redis服务和name参数,name的类型不是类,无法从容器中查找
    public function __construct(Redis $redis, $name, $default = '默认值')
    {
        $this->redis = $redis;
        $this->name = $name;
        $this->default = $default;
    }
}

$container = new ParametersContainer();
// 绑定Redis服务
$container->singleton(Redis::class, function () {
    return new Redis();
});
// 构建Cache类
$cache = $container->make(Cache::class, ['name' => 'test']);
var_dump($cache);

提示:实际上,Laravel容器的 build 方法并没有第二个参数 $parameters ,它是用类属性来维护自定义参数。原理都是一样的,只是实现方式不一样。这里为了方便理解,不引入过多概念。

7. 服务别名

别名可以理解成 小名外号 。服务别名就是给已绑定的服务设置一些 外号 ,使我们通过 外号 也能找到该服务。

这个就比较简单了,我们增加一个新的数组 $aliases ,用来存储别名。再增加一个方法 alias ,用来让外部注册别名。

唯一需要我们修改的地方,就是在 make 时,要先从 $aliases 中找到真实的服务名。

class AliasContainer extends ParametersContainer
{
    // 服务别名
    protected $aliases = [];

    // 给服务绑定一个别名
    public function alias($alias, $name)
    {
        $this->aliases[$alias] = $name;
    }

    // 获取服务
    public function make($name, array $parameters = [])
    {
        // 先用别名查找真实服务名
        $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;

        return parent::make($name, $parameters);
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

$container = new AliasContainer();

// 绑定服务
$container->instance('text', '这是一个字符串');
// 给服务注册别名
$container->alias('string', 'text');
$container->alias('content', 'text');

var_dump($container->make('string'));
var_dump($container->make('content'));

8. 扩展绑定

有时候我们需要给已绑定的服务做一个包装,这时候就用到扩展绑定了。我们先看一个实际的用法,理解它的作用后,才看它是如何实现的。

// 绑定日志服务
$container->singleton('log', new Log());

// 对已绑定的服务再次包装
$container->extend('log', function(Log $log){
    // 返回了一个新服务
    return new RedisLog($log);
});

现在我们看它是如何实现的。增加一个 $extenders 数组,用来存放扩展器。再增加一个 extend 方法,用来注册扩展器。

然后在 make 方法返回 $instance 之前,按顺序依次调用之前注册的扩展器。

class ExtendContainer extends AliasContainer
{
    // 存放扩展器的数组
    protected $extenders = [];

    // 给服务绑定扩展器
    public function extend($name, $extender)
    {
        if (isset($this->instances[$name])) {
            // 已经实例化的服务,直接调用扩展器
            $this->instances[$name] = $extender($this->instances[$name]);
        } else {
            $this->extenders[$name][] = $extender;
        }
    }

    // 获取服务
    public function make($name, array $parameters = [])
    {
        $instance = parent::make($name, $parameters);

        if (isset($this->extenders[$name])) {
            // 调用扩展器
            foreach ($this->extenders[$name] as $extender) {
                $instance = $extender($instance);
            }
        }

        return $instance;
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

class Redis
{
    public $name;

    public function __construct($name = 'default')
    {
        $this->name = $name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

$container = new ExtendContainer();

// 绑定Redis服务
$container->singleton(Redis::class, function () {
    return new Redis();
});

// 给Redis服务绑定一个扩展器
$container->extend(Redis::class, function (Redis $redis) {
    $redis->setName('扩展器');
    return $redis;
});
$redis = $container->make(Redis::class);
var_dump($redis->name);

9. 上下文绑定

有时侯我们可能有两个类使用同一个接口,但希望在每个类中注入不同的实现,例如两个控制器,分别为它们注入不同的 Log 服务。

class ApiController
{
    public function __construct(Log $log)
    {
    }
}

class WebController
{
    public function __construct(Log $log)
    {
    }
}

最终我们要用以下方式实现:

// 当ApiController依赖Log时,给它一个RedisLog
$container->addContextualBinding('ApiController','Log',new RedisLog());

// 当WebController依赖Log时,给它一个FileLog
$container->addContextualBinding('WebController','Log',new FileLog());

为了更直观更方便更语义化的使用,我们把这个过程改成链式操作:

$container->when('ApiController')
        ->needs('Log')
        ->give(new RedisLog());

我们增加一个 $context 数组,用来存储上下文。同时增加一个 addContextualBinding 方法,用来注册上下文绑定。以 ApiController 为例, $context 的真实模样是:

$context['ApiController']['Log'] = new RedisLog();

然后 build 方法实例化类时,先从上下文中查找依赖参数,就实现了上下文绑定。

接下来,看看链式操作是如何实现的。

首先定义一个类 Context ,这个类有两个方法, needsgive

然后在容器中,增加一个 when 方法,它返回一个 Context 对象。在 Context 对象的 give 方法中,我们已经具备了注册上下文所需要的所有参数,所以就可以在 give 方法中调用 addContextualBinding 来注册上下文了。

class ContextContainer extends ExtendContainer
{
    // 依赖上下文
    protected $context = [];

    // 构建一个类,并自动注入服务
    public function build($class, array $parameters = [])
    {
        $reflector = new ReflectionClass($class);

        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            // 没有构造函数,直接new
            return new $class();
        }

        $dependencies = [];

        // 获取构造函数所需的参数
        foreach ($constructor->getParameters() as $dependency) {

            if (isset($this->context[$class]) && isset($this->context[$class][$dependency->getName()])) {
                // 先从上下文中查找
                $dependencies[] = $this->context[$class][$dependency->getName()];
                continue;
            }

            if (isset($parameters[$dependency->getName()])) {
                // 从自定义参数中查找
                $dependencies[] = $parameters[$dependency->getName()];
                continue;
            }

            if (is_null($dependency->getClass())) {
                // 参数类型不是类或接口时,无法从容器中获取依赖
                if ($dependency->isDefaultValueAvailable()) {
                    // 查找默认值,如果有就使用默认值
                    $dependencies[] = $dependency->getDefaultValue();
                } else {
                    // 无法提供类所依赖的参数
                    throw new Exception('找不到依赖参数:' . $dependency->getName());
                }
            } else {
                // 参数类型是一个类时,就用make方法构建该类
                $dependencies[] = $this->make($dependency->getClass()->name);
            }
        }

        return $reflector->newInstanceArgs($dependencies);
    }

    // 绑定上下文
    public function addContextualBinding($when, $needs, $give)
    {
        $this->context[$when][$needs] = $give;
    }

    // 支持链式方式绑定上下文
    public function when($when)
    {
        return new Context($when, $this);
    }
}

class Context
{
    protected $when;

    protected $needs;

    protected $container;

    public function __construct($when, ContextContainer $container)
    {
        $this->when = $when;
        $this->container = $container;
    }

    public function needs($needs)
    {
        $this->needs = $needs;

        return $this;
    }

    public function give($give)
    {
        // 调用容器绑定依赖上下文
        $this->container->addContextualBinding($this->when, $this->needs, $give);
    }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

class Dog
{
    public $name;

    public function __construct($name)
    {
        $this->name = $name;
    }
}

class Cat
{
    public $name;

    public function __construct($name)
    {
        $this->name = $name;
    }
}

$container = new ContextContainer();

// 给Dog类设置上下文绑定
$container->when(Dog::class)
    ->needs('name')
    ->give('小狗');
// 给Cat类设置上下文绑定
$container->when(Cat::class)
    ->needs('name')
    ->give('小猫');

$dog = $container->make(Dog::class);
$cat = $container->make(Cat::class);
var_dump('Dog:' . $dog->name);
var_dump('Cat:' . $cat->name);

10. 完整代码

class Container
{
    // 已绑定的服务
    protected $instances = [];
    // 已绑定的回调函数
    protected $bindings = [];
    // 服务别名
    protected $aliases = [];
    // 存放扩展器的数组
    protected $extenders = [];
    // 依赖上下文
    protected $context = [];

    // 绑定服务实例
    public function instance($name, $instance)
    {
        $this->instances[$name] = $instance;
    }

    // 绑定服务
    public function bind($name, $instance, $shared = false)
    {
        if ($instance instanceof Closure) {
            // 如果$instance是一个回调函数,就绑定到bindings。
            $this->bindings[$name] = [
                'callback' => $instance,
                // 标记是否单例
                'shared' => $shared
            ];
        } else {
            // 调用make方法,创建实例
            $this->instances[$name] = $this->make($name);
        }
    }

    // 绑定一个单例
    public function singleton($name, $instance)
    {
        $this->bind($name, $instance, true);
    }

    // 给服务绑定一个别名
    public function alias($alias, $name)
    {
        $this->aliases[$alias] = $name;
    }

    // 给服务绑定扩展器
    public function extend($name, $extender)
    {
        if (isset($this->instances[$name])) {
            // 已经实例化的服务,直接调用扩展器
            $this->instances[$name] = $extender($this->instances[$name]);
        } else {
            $this->extenders[$name][] = $extender;
        }
    }

    // 获取服务
    public function make($name, array $parameters = [])
    {
        // 先用别名查找真实服务名
        $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;

        if (isset($this->instances[$name])) {
            return $this->instances[$name];
        }

        if (isset($this->bindings[$name])) {
            // 执行回调函数并返回
            $instance = call_user_func($this->bindings[$name]['callback']);

            if ($this->bindings[$name]['shared']) {
                // 标记为单例时,存储到服务中
                $this->instances[$name] = $instance;
            }
        } else {
            // 使用build方法构建此类
            $instance = $this->build($name, $parameters);
        }

        if (isset($this->extenders[$name])) {
            // 调用扩展器
            foreach ($this->extenders[$name] as $extender) {
                $instance = $extender($instance);
            }
        }

        return $instance;
    }

    // 构建一个类,并自动注入服务
    public function build($class, array $parameters = [])
    {
        $reflector = new ReflectionClass($class);

        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            // 没有构造函数,直接new
            return new $class();
        }

        $dependencies = [];

        // 获取构造函数所需的参数
        foreach ($constructor->getParameters() as $dependency) {

            if (isset($this->context[$class]) && isset($this->context[$class][$dependency->getName()])) {
                // 先从上下文中查找
                $dependencies[] = $this->context[$class][$dependency->getName()];
                continue;
            }

            if (isset($parameters[$dependency->getName()])) {
                // 从自定义参数中查找
                $dependencies[] = $parameters[$dependency->getName()];
                continue;
            }

            if (is_null($dependency->getClass())) {
                // 参数类型不是类或接口时,无法从容器中获取依赖
                if ($dependency->isDefaultValueAvailable()) {
                    // 查找默认值,如果有就使用默认值
                    $dependencies[] = $dependency->getDefaultValue();
                } else {
                    // 无法提供类所依赖的参数
                    throw new Exception('找不到依赖参数:' . $dependency->getName());
                }
            } else {
                // 参数类型是一个类时,就用make方法构建该类
                $dependencies[] = $this->make($dependency->getClass()->name);
            }
        }

        return $reflector->newInstanceArgs($dependencies);
    }

    // 绑定上下文
    public function addContextualBinding($when, $needs, $give)
    {
        $this->context[$when][$needs] = $give;
    }

    // 支持链式方式绑定上下文
    public function when($when)
    {
        return new Context($when, $this);
    }
}

class Context
{
    protected $when;

    protected $needs;

    protected $container;

    public function __construct($when, Container $container)
    {
        $this->when = $when;
        $this->container = $container;
    }

    public function needs($needs)
    {
        $this->needs = $needs;

        return $this;
    }

    public function give($give)
    {
        // 调用容器绑定依赖上下文
        $this->container->addContextualBinding($this->when, $this->needs, $give);
    }
}

以上所述就是小编给大家介绍的《如何实现Laravel的服务容器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

通灵芯片

通灵芯片

Daniel Hillis / 崔良沂 / 上海世纪出版集团 / 2009-1 / 19.80元

本书深入浅出地阐述了计算机科学中许多基本而重要的概念,包括布尔逻辑、有限自动机、编程语言、图灵机的普遍性、信息论、算法、并行计算、量子计算、神经网络、机器学习乃至自组织系统。 作者高屋建瓴式的概括,既不失深度,又妙趣横生,相信读者读后会有很多启发。 目录: 序言:石的奇迹 第一章 通用件 第二章 万能积木 第三章 程序设计 第四章 图灵机的普适性 第......一起来看看 《通灵芯片》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具