Ruby 自检系列

作者: 臻有一扑 | 来源:发表于2016-05-14 16:14 被阅读73次

    本文基于 ruby-triviaruby-style完成,节选部分问题,并更进一步探究原理,用以加强自身对ruby基础的理解和应用。

    Language Characteristics and Core Objects

    Q: Is Ruby a statically typed or a dynamically typed language?
    A: Dynamically typed since type checking is done at runtime.

    Q: Is Ruby a strongly typed or a weakly typed language?
    A: Strongly typed since an object's type is checked before an operation is performed on it.

    Q: What is the difference between a statement and an expression in Ruby?
    A: All statements are expressions in Ruby since all statements return a value.
    (expressions > statements)

    Data Types

    Q: Does String include the Enumerable module?
    A: No.

    Q: What method might you use to enumerate over a string?
    A: String#each_char

    Hash

    Q: How might you specify a default value for a hash?
    A: Pass the default values as arguments to ::new on initialization or change the default directly with the method Hash#default. You may also provide a default at the time of query with Hash#fetch.
    C: 在某些情况下使用Hash#fetch是比较合适的:

    • 当处理应该存在的哈希键时,使用 Hash#fetch
    heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' }
    
    # 差 - 如果我们打错了哈希键,则难以发现这个错误
    heroes[:batman] # => 'Bruce Wayne'
    heroes[:supermann] # => nil
    
    # 好 - fetch 会抛出 KeyError 使这个错误显而易见
    heroes.fetch(:supermann)
    
    • 当为哈希键的值提供默认值时,倾向使用 Hash#fetch 而不是自定义逻辑。
    batman = { name: 'Bruce Wayne', is_evil: false }
    
    # 差 - 如果仅仅使用 || 操作符,那么当值为假时,我们不会得到预期结果
    batman[:is_evil] || true # => true
    
    # 好 - fetch 在遇到假值时依然可以正确工作
    batman.fetch(:is_evil, true) # => false
    
    • 当提供默认值的求值代码具有副作用或开销较大时,倾向使用 Hash#fetch 的block形式
    batman = { name: 'Bruce Wayne' }
    
    # 差 - 此形式会立即求值,如果调用多次,可能会影响程序的性能
    batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers 开销较大
    
    # 好 - 此形式会惰性求值,只有抛出 KeyError 时,才会产生开销
    batman.fetch(:powers) { obtain_batman_powers }
    

    Q: Does Hash use #== or #eql? to compare hash keys?
    A: #eql?
    C: Why? What's the different between them?
    Hash使用#eql?来做比较。而Numeric则是通过 #==做比较,而不是#eql?。在这种情况下,#eql?会做更严格的比较。
    对于Object的对象, #eql? 是跟 #== 一样的。由此可知,Numeric是例外

    equal? 则检查两者是否是同一对象(相同值,相同type)。

    #对于 Numeric
    x = 1
    puts x.object_id  # => 3
    
    y = 1
    puts y.object_id  # => 3
    
    z = 1.0
    puts z.object_id  # => -36028797018963966
    
    x == y  # true  同一个object
    x.eql? y  # true 同一个object
    
    x == z   #true 不同object,但是content相同
    x.eql? z  #false 不同object,并且是不同的type,x是Fixnum,y是Float
    
    #对于 Array object
    x = [1, 2, 3]
    y = [3, 2, 1]
    z = [1, 2, 3]
    
    x.object_id  # => 70244601475800
    y.object_id  # => 70244601492000
    x.eql?(y) #=> false
    
    x.object_id  # => 70244601475800
    z.object_id  # => 70244601475800
    x.eql?(z) #=> true
    
    x.object_id  # => 70244601475800
    s = y.sort  # => [1, 2, 3]
    s.object_id  # => 70244618664060
    x.class # => Array
    y.class # => Array
    x == s #=> true  相同value
    x.eql?(s) #=> true  相同value和type
    x.equal?s  #=> false 相同value和type,但是不是同一个对象
    

    Q: What's is the precedence of &&, and, or, =, ||?
    A: && > || > = > (and, or)

    Q: . vs ::
    A: 调用类方法时,他们没有区别。但是使用::可以访问constant和namespace

    Q: != vs <=>(spaceship)
    A: != 不等于。 <=>,左边大于右边则返回1,等于返回0,小于返回-1

    Q: What's true and false in ruby?
    A: Everything in Ruby is true except false and nil.
    TrueClass, FalseClass, but can't create a new instance for them. true and false are the only things we can get.
    We can recognize them by either using nil? or false on == left-hand.

    Q: What is duck type?
    A: Walk like a duck, yip like a duck, it's a duck. In an other word, interface over type.

    Q:

    h = Hash.new
    h[:s] = 'ok'
    

    Can you retrive 'ok' by h['s'] ?
    A: You cannot. Since the Hash class in Ruby’s core library retrieves values by doing a standard == comparison on the keys. This means that a value stored for a Symbol key (e.g. :my_value) cannot be retrieved using the equivalent String (e.g. ‘my_value’). On the other hand, HashWithIndifferentAccess treats Symbol keys and String keys as equivalent so that the following would work:

    Q:#joins vs #includes in Rails ActiveRecord
    A: #joins performs an inner join between two tables.

    orders = Order.joins(:listing)
    => SELECT "orders".* FROM "orders" INNER JOIN "listings" ON "listings"."id" = "orders"."listing_id"
    

    内连接。对于 T1 中的每一行 R1 ,如果能在 T2 中找到一个或多个满足连接条件的行, 那么这些满足条件的每一行都在连接表中生成一行。In another word, It will retrieve all records where listing_id (of orders table) is equal to listing.id (listings table)

    这时候并没有listing的数据被取出。所有如果要取某个order的关联关系listing的属性,还是回去查一次数据库:

    order_1 = orders.first
    //上个命令的执行结果被存在内存中了, 直接读取了。
    => #<Order id: 1, customer_id: 1, listing_id: 1, product_id: 1, created_at: "2016-04-08 02:30:16", updated_at: "2016-04-08 02:30:49", status: "paid">
    
    order_1.listing.address
    //但具体关联到某个order上的listing信息还在数据库中,需要进行一次查询。
    => SELECT  "listings".* FROM "listings" WHERE "listings"."id" = $1 LIMIT 1  [["id", 1]]
    

    #includes performs a left outer like join between the two tables.

    orders = Order.includes(:listing)
    => SELECT "orders".* FROM "orders"
    => SELECT "listings".* FROM "listings" WHERE "listings"."id" IN (1, 2, 3, 4)
    

    这时候执行的是类似于左外连接。
    左外连接:首先执行一次内连接。然后为每一个 T1 中无法在 T2 中找到匹配的行生成一行, 该行中对应 T2 的列用 NULL 补齐。因此,生成的连接表里总是包含来自 T1 里的每一行至少一个副本。
    为什么说是‘类似左外连接’?正常的左外连接应该是这样写的:

    SELECT "orders".* FROM "orders" LEFT JOIN "listings" ON "listings"."id" = "orders"."listing_id";
    

    但是在rails中我们并没有看到。这是因为在rails4中,我们需要手动写LEFT OUTER JOIN的sql查询语句:

    orders = Order.joins('LEFT OUTER JOIN "listings" ON "listings"."id" = "orders"."listing_id"')
    

    其实rails帮我们做的是先把所有orders表里的记录取出。然后再在listings表中取出可以跟orders表记录匹配的记录,并存在内存中。

    好消息是rails5中加入了一个新方法#letf_outer_joins来执行LEFT OUTER JOINS.

    这时候我们再来执行一次查询某个order的关联关系listings上的属性:

    order_1 = orders.first
    //从内存中取
    => #<Order id: 1, customer_id: 1, listing_id: 1, product_id: 1, created_at: "2016-04-08 02:30:16", updated_at: "2016-04-08 02:30:49", status: "paid">
    
    order_1.listing.address
    //因为listings表中的相应记录已经在第一步中被取出,所以这时候直接从内存中读即可。
     => {"city"=>"Nantes (44000)", "street_number"=>"", "region"=>"Pays de la Loire", "street"=>""}
    

    可见,再次查询的时候并不会去读数据库。

    相关文章

      网友评论

        本文标题:Ruby 自检系列

        本文链接:https://www.haomeiwen.com/subject/rggmrttx.html