Ruby极速入门

栏目: Ruby · 发布时间: 5年前

内容简介:因为工作关系需要用到Ruby和Rails,于是在端午假期里花了点时间快速的学习了下。这里做一个简单的记录。在Ruby中,所有的一切都是对象。每个对象都有一个唯一的对象标识符(object identifier,缩写为object ID)Ruby名字用来引用常用、变量、方法、类和模块。命名规则是第一个字符必须是大写字母、小写字母或下划线,后面跟任意个命名用字符:大写字母、小写字母、下划线或者数字的任意组合

因为工作关系需要用到 Ruby 和Rails,于是在端午假期里花了点时间快速的学习了下。这里做一个简单的记录。

基础数据类型

在Ruby中,所有的一切都是对象。每个对象都有一个唯一的对象标识符(object identifier,缩写为object ID)

  • 整数类型(numbers)

    整数是 Fixnum 类和 Bignum 类的对象。 Fixnum 对象可以容纳比本机字节长少一位的整数。当一个 Fixnum 超过这个范围时,它将会自动转换成 Bignum 对象, Bignum 对象的表示范围仅受可用内存大小的限制。如果 Bignum 对象的操作结果可以用 Fixnum 表示,那么结果将以 Fixnum 类型返回

    整数由一个可选的符号标记、一个可选的进制指示符( 0 代表八进制, 0d 代表十进制, 0x 代表十六进制, 0b 代表二进制)和一个相应进制的字符串组成。数字串中的下划线会被忽略

    123456                  => 123456               # Fixnum
    0d123456                => 123456               # Fixnum
    123_456                 => 123456               # Fixnum -忽略下划线
    -543                    => -543                 # Fixnum -负数
    0xaabb                  => 43707                # Fixnum -十六进制
    0377                    => 255                  # Fixnum -八进制
    -0b10_1010              => -42                  # Fixnum -二进制(负数)
    123_456_789_123_456_789 => 123456789123456789   # Bignum
    复制代码
  • 浮点数类型(floats)

    一个带有小数点或指数的数字字面量被认为是 Float 对象, Float 对象和本机上的 double 数据类型大小一样。小数点的前后都必须至少要有一个数字。像 1.e3 这样的串将试图调用 Fixnum 类的 e3 方法

    12.34                   => 12.34
    -0.1234e2               => -12.34
    1234e-2                 => 12.34
    复制代码
  • 字符串(strings)

    Ruby提供了多种机制来生成字面量字符串。每种机制都产生 String 类型的对象。不同机制的区别在于如何分隔字符串以及字面量内容中会进行哪些替换

    单引号引起来的字符串字面量执行的替换最少。例如 'stuff'%q/stuff/ ,这两者都会将 \\ 转换为单个反斜杠, \' 转换为单引号。所有其他的反斜杠都不会进行转换

    'hello'                     => hello
    'a backslash \'\\\''        => a backslash '\'
    %q/simple string/           => simple string
    %q(nesting (really) works)  => nesting (really) works
    %q no_blanks_here ;         => no_blanks_here
    复制代码

    双引号字符串,例如 "stuff"%Q/stuff/%/stuff/ 还执行额外的替换

    a = 123
    "\123mile"                  => Smile
    "Say \"Hello\""             => Say "Hello"
    %Q!"I said 'nuts'," I said! => "I said 'nuts'," I said
    %Q{Try #{a+1}, not #{a-1}}  => Try 124, not 122
    %<Try #{a+1}, not #{a-1}>   => Try 124, not 122
    "Try #{a+1}, not #{a-1}"    => Try 124, not 122
    %{#{ a=1; b=2; a+b }}       => 3
    复制代码
  • 区间(ranges)

    区间使用表达式 expr..exprexpr...expr 来构建 Range 对象。两个点的形式是闭合区间(包括右端的值),而三个点的形式是半开半闭的(不包括右端的值)

    > (1..5).each {|n| puts n}
     1
     2
     3
     4
     5
    > (1...5).each {|n| puts n}
     1
     2
     3
     4
    复制代码
  • 数组(arrays)

    数组类的字面量是在方括号间由逗号分隔的一连串对象引用组成。尾部逗号将被忽略。数组也可以用简写形式 %w%W 来构成

    arr = [ :fred, 10, 3.14, "this is a string", "pebbles", ]
            => [:fred, 10, 3.14, "this is a string", "pebbles"]
    
    arr = %w( fred wilma barney betty great\ gazoo )
            => ["fred", "wilma", "barney", "betty", "great gazoo"]
    
    arr = %w( Hey!\tIt is now -#{Time.now}-)
            => ["Hey!\\tIt", "is", "now", "-\#{Time.now}-"]
    
    arr = %W( Hey!\tIt is now -#{Time.now}-)
            => ["Hey!\tIt", "is", "now", "-2019-06-09 11:35:50 +0800-"]
    复制代码

    数组对象在方括号之间提供索引便可以访问单个元素,Ruby的数组索引从零开始

    a = [1, 'cat', 3.14]    => 三个元素数组
    a[0]                    => 1
    a[2] = nil              => 设置第三个元素
    a                       => [1, "cat", nil]
    复制代码
  • 散列表(hashes)

    Ruby的Hash字面量是由花括号中的键/值对列表构成,由逗号分隔键/值对,键和值之间由 => 序列分隔。尾部的逗号将被忽略。并不要求一个具体的散列表中的所有 keyvalue 必须为相同类型。散列表中的键必须能够响应 hash 消息并返回一直散列码(hash code),且键对应的散列码不能改变。散列表中的键也必须能用 eql? 来比较

    colors = {
      "red"   => 0xf00,
      "green" => 0x0f0,
      "blue"  => 0x00f,
    }
    复制代码

    散列表也使用与数组相同的方括号表示法来进行索引

    colors["red"]           => 3840
    colors["white"] = 0xfff => 4095
    colors                  => {"red"=>3840, "green"=>240, "blue"=>15, "white"=>4095}
    复制代码
  • 符号(symbols)

    Ruby的符号是一个对应字符串(通常是一个名字)的标识符。可以通过在名字前加一个冒号来构造该名字的符号,也可以在任意字符串字面量前加一个冒号来创建该字符串的符号。在双引号的字符串会发生替换。不管程序如何使用名字,一个具体的名字或者字符串总是产生同样的符号

    :Object
    :my_variable
    :"Ruby rules"
    a = 'cat'
    :'catsup'       => :catsup
    :"#{a}sup"      => :catsup
    :'#{a}sup'      => :"\#{a}sup"
    复制代码

    其他语言称这个过程为 interning ,而称符号为原子

  • 正则表达式(regular expressions)

    正则表达式的字面量是 Regexp 类型的对象。可以通过调用 Regexp.new 构造函数显示创建正则表达式,也可以使用字面量形式 /pattern/%r{pattern} 隐式创建

    /pattern/
    /pattern/options
    %r{pattern}
    %r{pattern}options
    Regexp.new('pattern' [, options])
    复制代码

    一旦有了正则表达式对象,可以使用 Regexp#match(string) 或匹配操作符 =~ (肯定匹配)和 !~ (否定匹配)对字符串进行匹配

    name = "Fats Waller"
    name =~ /a/             => 1
    name =~ /z/             => nil
    /a/ =~ name             => 1
    复制代码

    匹配操作符是有副作用的,它们会设置一些Ruby的变量。 $& 得到与模式匹配的那部分字符串, $``得到匹配之前的那部分字符串,而 $'`得到匹配之后的那部分字符串

    $&                      => a            模式匹配的字符串
    $`                      => F            模式匹配之前的字符串
    $'                      => ts Waller    模式匹配之后的字符串
    复制代码

变量与常量

Ruby名字用来引用常用、变量、方法、类和模块。命名规则是第一个字符必须是大写字母、小写字母或下划线,后面跟任意个命名用字符:大写字母、小写字母、下划线或者数字的任意组合

  • 局部变量名(local variable)

    由小写字母后跟命名用字符组合。通常来说,使用 下划线 而不是 camelCase 来书写含多个词的名字

    three_two_one = 321
    复制代码
  • 实例变量名(instance variable)

    以一个 @ 开始,后跟一个名字。在 @ 后用小写字母是个不错的做法

    @name   @_    @size
    复制代码
  • 类变量名(class variable)

    @@ 开始,后跟一个名字

    @@name  @@_   @@size
    复制代码
  • 常量名(const variable)

    以一个大写字母开始,后跟多个命名用字符。类名字和模块名字都是常量,所以遵循常量的命名传统。习惯上,常量对象引用一般由大写字母和下划线组成,而类和模块名字是 MixedCase

    module Math
      ALMOST_PI = 22.0/7.0
    end
    
    class BigBlob
    end
    复制代码
  • 全局变量名(global variable)

    由美元符 $ 后跟命名用字符组成。全局变量在程序中任何地方都可以修改,一般不推荐使用全局变量

    $temp = "this is a global variable"
    复制代码
  • 多重赋值

    Ruby可以对一组变量同时赋值。如果变量前加上 * ,则Ruby会将未分配的值封装为数组赋值给该变量

    a, b, c = 1, 2, 3
    a, b, *c = 1, 2, 3, 4, 5    => a=1, b=2, c=[3,4,5]
    a, *b, c = 1, 2, 3, 4, 5    => a=1, b=[2,3,4], c=5
    复制代码

条件判断

  • 条件与真假

    在Ruby中,只有 falsenil 为假,除此以外的所有值都代表真。另外,在Ruby中还有个约定俗成的规则,为了使程序容易理解,返回真假的方法都要以 ? 结尾

  • 逻辑运算符

    Ruby中的逻辑运算符有 &&||! ,同时还有意思相同,但优先级略低的逻辑运算符 andornot 。通常前者用于逻辑判断,后者用于流程控制

    条件1与条件2必须都为真,表达式返回真

    条件1 && 条件2
    条件1 and 条件2
    复制代码

    条件1或条件2其中一个为真,表达式则返回真

    条件1 || 条件2
    条件1 or 条件2
    复制代码

    取相反条件,返回条件表达式的相反逻辑

    !条件
    not 条件
    复制代码
  • 比较操作符

    Ruby的语法定义了比较操作符 =====<=><<=>>==~ 。所有这些操作符都是由方法来实现的。习惯上,Ruby也使用标准方法 eql?equal? 。其中 ===~ 都有相反的形式 !=!~

  • if语句

    if语句是最基本的条件判断语句,形式如下:

    if 条件1 then
      处理1
    elseif 条件2 then
      处理2
    elseif 条件3 then
      处理3
    else
      处理4
    end
    复制代码

    可以省略 then

  • unless语句

    unless语句的用法刚好与if相反,形式如下:

    unless 条件 then
      处理1
    else
      处理2
    end
    复制代码

    可以省略 then

  • if修饰符与unless修饰符

    ifunless 可以像下面这样写在希望执行的代码后面

    puts "a > b" if a > b
    复制代码

    在使用修饰符写法时,要注意程序的易读性

  • 三元运算符

    三元运算符是 if...else 的简写形式

    条件 ? 处理1 : 处理2
    复制代码
  • case语句

    当比较对象只有一个,根据这个对象值的不同,执行不同的处理时,使用case语句会使程序更简单,更便于理解

    case 比较对象
    when 值1 then
      处理1
    when 值2 then
      处理2
    when 值3 then
      处理3
    else
      处理4
    end
    复制代码

    可以省略 then

循环

  • times方法

    times方法可以轻松实现单纯执行一定次数的处理

    循环次数.times do
      希望循环的处理
    end
    复制代码

    do...end 部分可以用 {...} 代替

    循环次数.times {
        希望循环的处理
    }
    复制代码

    使用times方法时也可以获知块中当前的循环次数

    5.times do |i|
      puts "第 #{i} 次的循环"
    end
    复制代码
  • for语句

    for并不是方法,而是Ruby提供的循环控制语句。形式如下:

    for 变量 in 开始值..结束值 do
      希望循环的处理
    end
    
    for 变量 in 对象 do
      希望循环的处理
    end
    复制代码

    可以省略 do

    sum = 0
    for i in 1..5
      sum = sum + i
    end
    puts sum
    
    names = ["awk", "Perl", "Python", "Ruby"]
    for name in names
      puts name
    end
    复制代码
  • while语句

    不管哪种类型的循环,while语句都可以胜任,while语句的结构如下:

    while 条件 do
      希望循环的处理
    end
    复制代码

    可以省略 do

    i = 1
    while i < 3
      puts i
      i += 1
    end
    复制代码
  • until语句

    until是与while相对的语句,until语句的结构与while语句完全一样,只是条件判断刚好相反,不满足条件时才执行循环处理

    until 条件 do
      希望循环的处理
    end
    复制代码

    可以省略 do

    sum = 0
    i = 1
    until sum >= 50
      sum += i
      i += 1
    end
    puts sum
    复制代码
  • each方法

    each方法是将对象集合中的对象逐个取出,这与for语句循环取出数组元素非常相似

    对象.each do |变量|
      希望循环的处理
    end
    
    对象.each {|变量|
      希望循环的处理
    }
    复制代码

    在Ruby内部,for语句是用each方法来实现的。因此可以使用each方法的对象,同样也可以指定为for语句的循环对象

    sum = 0
    (1..5).each do |i|
      sum = sum + i
    end
    puts sum
    复制代码
  • loop方法

    还有一种循环的方法,没有终止循环的条件,只是不断执行循环处理

    loop do
      希望循环的处理
    end
    复制代码

    程序不小心执行了死循环,可以使用 Ctrl + C 来强行终止程序

    loop do
      puts "Ruby"
    end
    复制代码
  • 循环控制

    break 终止处理,跳出循环

    next 跳到下一次循环

    redo 在相同的条件下重复刚才的处理。一般很少使用

方法

  • 调用

    简单的方法调用

    对象.方法名(参数1, 参数2, ..., 参数n)
    复制代码

    对象也被称为 接收者 (receiver)。在面向对象的世界中,调用方法被称为“向对象发送消息(message)”,调用的结果就是“对象接收(receive)了消息”。也就是说,方法的调用就是几个参数连同消息一起发送给对象的过程

    带块的方法调用

    对象.方法名(参数, ...) do |变量1, 变量2, ...|
      块内容
    end
    
    对象.方法名(参数, ...) { |变量1, 变量2, ...|
      块内容
    }
    复制代码

    |~| 中指定的变量称为 块变量 。在执行块的时候,块变量由方法传到块内部

    5.times do |i|
      puts i
    end
    复制代码
  • 定义

    一般方法的定义

    def 方法名(参数1, 参数2, ..., 参数n)
      希望执行的处理
    end
    复制代码

    我们可以用 return 语句指定方法的返回值

    return 值
    复制代码

    通常方法的最后一个表达式结果就会成为方法的返回值,这时可以省略 return 语句

    通过 yield 关键字我们可以定义带块的方法

    def myloop
      while true
        yield
      end
    end
    
    num = 1
    myloop do
      puts "num is #{num}"
      break if num > 10
      num *= 2
    end
    复制代码

    可以通过 *变量名 的形式来定义参数个数不确定的方法。同样可以用 *数组 的形式来指定参数

    def foo(*args)
      args
    end
    
    p foo(1, 2, 3)      => [1, 2, 3]
    p foo(*[4, 5, 6])   => [4, 5, 6]
    复制代码

    定义带关键字参数的方法

    def 方法名(参数1: 参数1的值, 参数2: 参数2的值, ...)
      希望执行的处理
    end
    复制代码

    如果没有默认值的话,参数的值可以省略。我们还可以使用 **args 的形式来接收未定义的参数

    def volume(x:, y: 2, z: 4, **args)
      [x, y, z, args]
    end
    
    p volume(x: 2, y: 3)        => [2, 3, 4, {}]
    p volume({x: 3, k: 5})      => [3, 2, 4, {:k=>5}]
    复制代码

类和对象

  • 类和继承

    类表示对象的种类,Ruby中的对象一定都属于某个类。通过扩展已定义的类来创建新类称为 继承

    class 类名 [< 父类名]
      类的定义
    end
    复制代码

    类的首字母必须大写。类中的 initialize 方法比较特别。使用 new 方法生成新的对象时, initialize 方法会被调用,同时 new 方法的参数也会被原封不动地传给 initialize 方法

    class HelloWrold
      def initialize(myname = "Ruby")
        @name = myname
      end
    end
    
    bob = HelloWrold.new("Bob")
    ruby = HelloWrold.new
    复制代码
  • 存取器

    在Ruby中,从对象外部不能直接访问实例变量或对实例变量修改,需要通过方法来访问对象内部。Ruby为我们提供了简便的定义方法 attr_readerattr_writerattr_accessor

    attr_reader :name 只读(定义name方法)

    attr_writer :name 只写(定义name=方法)

    attr_accessor :name 读写(同时定义以上两个方法)

  • 类方法

    方法的接收者就是类本身的方法称为类方法

    class << 类名
      方法定义
    end
    
    def 类名.方法名(参数, ...)
    end
    复制代码

    下面展示四种定义方法

    class << HelloWorld
      def hello(name)
        puts "#{name} said hello."
      end
    end
    
    HelloWorld.hello("John")        => John said hello.
    
    class HelloWorld
      class << self
        def hello(name)
          puts "#{name} said hello."
        end
      end
    end
    
    HelloWorld.hello("Allen")       => Allen said hello.
    
    def HelloWorld.hello(name)
      puts "#{name} said hello."
    end
    
    HelloWorld.hello("Amber")       => Amber said hello.
    
    class HelloWorld
      def self.hello(name)
        puts "#{name} said hello."
      end
    end
    
    HelloWorld.hello("Chloe")       => Chloe said hello.
    复制代码
  • 类变量和常量

    类变量是该类所有实例的共享变量,这一点与常量类似

    class HelloWorld
      Version = "1.0"
      @@count = 0
    
      def self.count
        @@count
      end
    
      def initialize(myname = "Ruby")
        @name = myname
      end
    
      def hello
        @@count += 1
        puts "Hello, world. I am #{@name}."
      end
    end
    
    bob = HelloWorld.new("Bob")
    alice = HelloWorld.new("Alice")
    ruby = HelloWorld.new
    
    p HelloWorld.count      => 0
    bob.hello
    alice.hello
    ruby.hello
    p HelloWorld.count      => 3
    复制代码
  • 限制方法调用

    public 以实例方法的形式向外部公开该方法

    private 只能使用缺省接收者的方式调用该方法

    protected 在同一个类中时可将该方法作为实例方法调用

    class Point
      attr_accessor :x, :y
      protected :x=, :y=
    
      def initialize(x=0.0, y=0.0)
        @x, @y = x, y
      end
    
      public
    
      def swap(other)
        tmp_x, tmp_y = @x, @y
        @x, @y = other.x, other.y
        other.x, other.y = tmp_x, tmp_y
    
        return self
      end
    end
    
    p1 = Point.new(1.0, 2.0)
    p2 = Point.new(3.0, 4.0)
    
    p [p1.x, p1.y]
    p [p2.x, p2.y]
    
    p1.swap(p2)
    
    p [p1.x, p1.y]
    p [p2.x, p2.y]
    复制代码
  • alias与undef

    有时我们希望给已经存在的方法设置别名,这种情况下就需要使用 alias 方法

    alias 别名 原名
    alias :别名 :原名
    复制代码

    undef 用于删除已定义的方法

    undef 方法名
    undef :方法名
    复制代码
  • 模块

    模块是Ruby的特色功能之一

    module
      模块定义
    end
    复制代码

    模块不能拥有实例,模块不能被继承。如果希望把方法作为模块函数公开给外部使用,就需要用到 module_function 方法

    module HelloModule
      Version = "1.0"
    
      def hello(name)
        puts "Hello, #{name}"
      end
    
      module_function :hello
    end
    
    p HelloModule::Version      => 1.0
    HelloModule.hello("Alice")  => Hello, Alice
    
    include HelloModule
    Version                     => 1.0
    hello("Alice")              => Hello, Alice
    复制代码

    使用 Mix-in 就能将模块混合到类中。在定义类时使用 include ,模块中的方法、常量就能被类使用

    module M
      def meth
        "meth"
      end
    end
    
    class C
      include M
    end
    
    c = C.new
    p c.meth        => meth
    复制代码

错误与异常处理

  • 异常处理

    Ruby异常是 Exception 或其子类的对象,标准写法如下:

    begin
      可能会发生异常的处理
    rescue
      发生异常时的处理
    end
    复制代码

    Ruby中异常及其相关信息都是被作为对象来处理的。在 rescue 后指定变量名,可以获得异常对象

    begin
      可能会发生异常的处理
    rescue => 引用异常对象的变量
      发生异常时的处理
    end
    复制代码

    异常对象方法有 class 异常种类、 message 异常信息和 backtrace 异常发生的位置信息

  • 后处理

    不管是否发生异常都希望执行的处理,在Ruby中可以用 ensure 关键字来定义

    begin
      可能会发生异常的处理
    rescue => 变量
      发生异常时的处理
    ensure
      不管是否发生异常都要执行的处理
    end
    复制代码
  • 重试

    rescue 中使用 retry 后, begin 以下的处理会再重新执行一遍

    begin
      io = File.open(file)
    rescue
      sleep 10
      retry
    end
    复制代码
  • rescue修饰符

    if 修饰符、 unless 修饰符一样, rescue 也有对应的修饰符

    表达式1 rescue 表达式2
    复制代码

    如果表达式1中发生异常,表达式2的值就会成为整个表达式的值

    n = Integer(val) rescue 0
    复制代码
  • 异常补充

    如果异常处理的范围是方法的整个处理,也就是说整个方法内的程序都用 begin...end 括起来的话,就可以省略 begin 以及 end ,直接书写 rescueensure 部分的程序

    def foo
      方法处理
    rescue => 变量
      异常处理
    ensure
      后处理
    end
    
    class Foo
      类定义
    rescue => 变量
      异常处理
    ensure
      后处理
    end
    复制代码

    当存在多个种类的异常,且需要按异常的种类分别进行处理时,可以用多个 rescue 来分开处理

    begin
      可能发生异常的处理
    rescue Exception1, Exception2 => 变量
      异常处理1
    rescue Exception3 => 变量
      异常处理2
    rescue
      以上异常之外的异常处理
    end
    复制代码
  • 主动抛出异常

    raise 信息 - 抛出 RuntimeError 异常,并在新生成的异常对象中设置作为信息的字符串

    raise 异常类 - 抛出指定的异常

    raise 异常类, 信息 - 抛出指定的异常,并在新生成的异常对象中设置作为信息的字符串

    raise - 在 rescue 外抛出 RuntimeError 。在 rescue 中调用时,会再次抛出最后一次发生的异常( $! )

块(block)

块(block)就是在调用方法时能与参数一起传递的多个处理的集合

  • 控制块的执行

    同循环一样,块也使用 breaknextredo 作为流程控制

  • 将块封装为对象

    如上所述,在接收块的方法中执行块时,可以使用 yield 关键字。而Ruby还能把块当作对象处理。把块当对象处理后,就可以在接收块的方法之外的其他地方执行块,或者把块交给其他方法执行

    把块当对象操作时,我们需要用到 Proc 对象。定义 Proc 对象的典型方法是,调用 Proc.new 这个带块的方法。在调用 Proc 对象的 call 方法之前,块中定义的程序不会被执行

    hello = Proc.new do |name|
      puts "Hello, #{name}"
    end
    
    hello.call("World")
    hello.call("Ruby")
    复制代码

    在定义方法时,如果末尾的参数使用 &参数 的形式,Ruby就会自动把调用方法时传进来的块封装为Proc对象

    def total(from, to, &block)
      result = 0
      from.upto(to) do |num|
        if block
          result += block.call(num)
        else
          result += num
          end
      end
      return result
    end
    
    p total(1, 10)                      => 55
    p total(1, 10) {|num| num ** 2}     => 385
    复制代码
  • 作用域

    块内部的命名空间和块外部是共享的。在块外部定义的局部变量,在块中也可以继续使用。嗯!简而言之,这可以让Ruby轻松实现函数式的闭包。

    def counter
      cnt = 0
      Proc.new do
        cnt += 1
      end
    end
    
    c1 = counter
    c2 = counter
    
    c1.call         => 1
    c1.call         => 2
    c1.call         => 3
    c2.call         => 1
    c2.call         => 2
    复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

我看电商3:零售的变革

我看电商3:零售的变革

黄若 / 电子工业出版社 / 2018-4 / 49

在《我看电商3:零售的变革》之前,黄若先生的“我看电商”系列图书《我看电商》《再看电商》《我看电商2》,均为行业畅销书。黄若先生的图书有两大特如一是干货满满,二是观点鲜明。 “新零售”是眼下的热门词。在2017年里,数以万计的企业以“新零售”作为标识进入市场。但是社会上对“新零售“存在着各种模糊的定义和不尽相同的解读。 《我看电商3:零售的变革》中明确提出:新零售不应过分关注于渠道形式......一起来看看 《我看电商3:零售的变革》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具