简体   繁体   English

我该如何改善?

[英]How can I improve?

I was wondering if anyone could point out a cleaner better way to write my code which is pasted here. 我想知道是否有人可以指出一种更干净的更好的方式来编写粘贴在这里的代码。 The code scrapes some data from yelp and processes it into a json format. 该代码从yelp抓取了一些数据并将其处理为json格式。 The reason I'm not using hash.to_json is because it throws some sort of stack error which I can only assume is due to the hash being too large (It's not particularly large). 我不使用hash.to_json的原因是因为它引发某种堆栈错误,我只能认为是由于哈希值太大(它不是特别大)。

  • Response object = a hash 响应对象=哈希
  • text = the output which saves to file 文本=保存到文件的输出

Anyways guidance appreciated. 无论如何指导表示赞赏。

def mineLocation

  client = Yelp::Client.new
  request = Yelp::Review::Request::GeoPoint.new(:latitude=>13.3125,:longitude => -6.2468,:yws_id => 'nicetry')
  response = client.search(request) 
  response['businesses'].length.times do |businessEntry|
    text =""
     response['businesses'][businessEntry].each { |key, value|
        if value.class == Array 
          value.length.times { |arrayEntry|
            text+= "\"#{key}\":["
             value[arrayEntry].each { |arrayKey,arrayValue|
              text+= "{\"#{arrayKey}\":\"#{arrayValue}\"},"
             }
             text+="]"   
          }
        else 
              text+="\"#{arrayKey}\":\"#{arrayValue}\"," 
        end
       }
  end
 end

It looks like all your code is ultimately doing is this: 看起来您所有的代码最终都将执行以下操作:

require 'json'

def mine_location
  client = Yelp::Client.new
  request = Yelp::Review::Request::GeoPoint.new(latitude: 13.3125,
    longitude: -6.2468, yws_id: 'nicetry')
  response = client.search(request)

  return response['businesses'].to_json
end

Which works just fine for me. 这对我来说很好。

If, for whatever reason you really must write your own implementation of a JSON emitter, here's a couple of tips for you. 如果出于某种原因您确实必须编写自己的JSON发射器实现,那么这里有一些技巧。

The number 1 thing you completely ignore in your code is that Ruby is an object-oriented language, more specifically a class-based object-oriented language. 您在代码中完全忽略的第一件事是Ruby是一种面向对象的语言,更具体地说是一种基于类的面向对象的语言。 This means that problems are solved by constructing a network of objects that communicate with each other via message passing and respond to those messages by executing methods defined in classes to which those objects belong. 这意味着可以通过构建一个对象网络来解决问题,该对象网络通过消息传递相互通信,并通过执行在那些对象所属的类中定义的方法来响应这些消息。

This gives us a lot of power: dynamic dispatch, polymorphism, encapsulation and a ton of others. 这给了我们很多功能:动态调度,多态性,封装和其他功能。 Leveraging those, your JSON emitter would look something like this: 利用这些,您的JSON发射器将如下所示:

class Object
  def to_json; to_s                                                         end
end

class NilClass
  def to_json; 'null'                                                       end
end

class String
  def to_json; %Q'"#{to_s}"'                                                end
end

class Array
  def to_json; "[#{map(&:to_json).join(', ')}]"                             end
end

class Hash
  def to_json; "{#{map {|k, v| "#{k.to_json}: #{v.to_json}" }.join(', ')}}" end
end

mine_location looks just like above, except obviously without the require 'json' part. mine_location看起来与上面类似,但显然没有require 'json'部分。

If you want your JSON nicely formatted, you could try something like this: 如果您希望JSON格式正确,可以尝试执行以下操作:

class Object
  def to_json(*) to_s    end
end

class String
  def to_json(*) inspect end
end

class Array
  def to_json(indent=0)
    "[\n#{'  ' * indent+=1}#{
      map {|el| el.to_json(indent) }.join(", \n#{'  ' * indent}")
    }\n#{'  ' * indent-=1}]"
  end
end

class Hash
  def to_json(indent=0)
    "{\n#{'  ' * indent+=1}#{
      map {|k, v|
        "#{k.to_json(indent)}: #{v.to_json(indent)}"
      }.join(", \n#{'  ' * indent}")
    }\n#{'  ' * indent-=1}}"
  end
end

There's actually nothing Ruby-specific in this code. 这段代码实际上没有特定于Ruby的代码。 This is pretty much exactly what the solution would look like in any other class-based object-oriented language like Java, for example. 例如,这几乎完全是解决方案在任何其他基于类的面向对象的语言(如Java)中的外观。 It's just object-oriented design 101. 这只是面向对象的设计101。

The only thing which is language-specific is how to "modify" classes and add methods to them. 特定于语言的唯一事情是如何“修改”类并向其添加方法。 In Ruby or Python, you literally just modify the class. 在Ruby或Python中,您实际上只是在修改类。 In C# and Visual Basic.NET, you would probably use extension methods, in Scala you would use implicit conversions and in Java maybe the Decorator Design Pattern. 在C#和Visual Basic.NET中,您可能会使用扩展方法,在Scala中,您将使用隐式转换,而在Java中,您可能会使用Decorator设计模式。

Another huge problem with your code is that you are trying to solve a problem which is obviously recursive without actually ever recursing. 代码的另一个巨大问题是,您试图解决的问题显然是递归的,而实际上并没有递归。 This just can't work. 这只是不能工作。 The code you wrote is basically Fortran-57 code: procedural with no objects and no recursion. 您编写的代码基本上是Fortran-57代码:无对象且无递归的过程。 Even just moving one step up from Fortran to, say, Pascal, gives you a nice recursive procedural solution: 即使是刚刚从Fortran的前进一步了,比方说,帕斯卡尔,为您提供了一个很好的递归程序解决方案:

def jsonify(o)
  case o
  when Hash
    "{#{o.map {|k, v| "#{jsonify(k)}: #{jsonify(v)}" }.join(', ')}}"
  when Array
    "[#{o.map(&method(:jsonify)).join(', ')}]"
  when String
    o.inspect
  when nil
    'null'
  else
    o.to_s
  end
end

Of course, you can play the same game with indentations here: 当然,您可以在此处使用缩进来玩同一游戏:

def jsonify(o, indent=0)
  case o
  when Hash
    "{\n#{'  ' * indent+=1}#{
      o.map {|k, v|
        "#{jsonify(k, indent)}: #{jsonify(v, indent)}"
      }.join(", \n#{'  ' * indent}") }\n#{'  ' * indent-=1}}"
  when Array
    "[\n#{'  ' * indent+=1}#{
      o.map {|el| jsonify(el, indent) }.join(", \n#{'  ' * indent}") }\n#{'  ' * indent-=1}]"
  when String
    o.inspect
  when nil
    'null'
  else
    o.to_s
  end
end

Here's the indented output of puts mine_location , produced using either the second (indented) version of to_json or the second version of jsonify , it doesn't really matter, they both have the same output: 下面是缩进输出puts mine_location ,无论使用的第二(缩进)版制作to_json或第二版本jsonify ,它其实并不重要,它们都具有相同的输出:

[
  {
    "name": "Nickies",
    "mobile_url": "http://mobile.yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ",
    "city": "San Francisco",
    "address1": "466 Haight St",
    "zip": "94117",
    "latitude": 37.772201,
    "avg_rating": 4.0,
    "address2": "",
    "country_code": "US",
    "country": "USA",
    "address3": "",
    "photo_url_small": "http://static.px.yelp.com/bpthumb/mPNTiQm5HVqLLcUi8XrDiA/ss",
    "url": "http://yelp.com/biz/nickies-san-francisco",
    "photo_url": "http://static.px.yelp.com/bpthumb/mPNTiQm5HVqLLcUi8XrDiA/ms",
    "rating_img_url_small": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_small_4.png",
    "is_closed": false,
    "id": "yyqwqfgn1ZmbQYNbl7s5sQ",
    "nearby_url": "http://yelp.com/search?find_loc=466+Haight+St%2C+San+Francisco%2C+CA",
    "state_code": "CA",
    "reviews": [
      {
        "rating": 3,
        "user_photo_url_small": "http://static.px.yelp.com/upthumb/ZQDXkIwQmgfAcazw8OgK2g/ss",
        "url": "http://yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ#hrid:t-sisM24K9GvvYhr-9w1EQ",
        "user_url": "http://yelp.com/user_details?userid=XMeRHjiLhA9cv3BsSOazCA",
        "user_photo_url": "http://static.px.yelp.com/upthumb/ZQDXkIwQmgfAcazw8OgK2g/ms",
        "rating_img_url_small": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_small_3.png",
        "id": "t-sisM24K9GvvYhr-9w1EQ",
        "text_excerpt": "So I know gentrification is supposed to be a bad word and all (especially here in SF), but the Lower Haight might benefit a bit from it. At least, I like...",
        "user_name": "Trey F.",
        "mobile_uri": "http://mobile.yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ?srid=t-sisM24K9GvvYhr-9w1EQ",
        "rating_img_url": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_3.png"
      },
      {
        "rating": 4,
        "user_photo_url_small": "http://static.px.yelp.com/upthumb/Ghwoq23_alkaXawgqj7dBA/ss",
        "url": "http://yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ#hrid:8xTNOC9L5ZXwGCMNYY-pdQ",
        "user_url": "http://yelp.com/user_details?userid=4F2QG3adYIUNXplqqp9ylA",
        "user_photo_url": "http://static.px.yelp.com/upthumb/Ghwoq23_alkaXawgqj7dBA/ms",
        "rating_img_url_small": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_small_4.png",
        "id": "8xTNOC9L5ZXwGCMNYY-pdQ",
        "text_excerpt": "This place was definitely a great place to chill. The atmosphere is very non-threatening and very neighborly. I thought it was cool that they had a girl dj...",
        "user_name": "Jessy M.",
        "mobile_uri": "http://mobile.yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ?srid=8xTNOC9L5ZXwGCMNYY-pdQ",
        "rating_img_url": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_4.png"
      },
      {
        "rating": 5,
        "user_photo_url_small": "http://static.px.yelp.com/upthumb/q0POOE3vv2LzNg1qN8MMyw/ss",
        "url": "http://yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ#hrid:pp33WfN_FoKlQKJ-38j_Ag",
        "user_url": "http://yelp.com/user_details?userid=FmcKafW272uSWXbUF2rslA",
        "user_photo_url": "http://static.px.yelp.com/upthumb/q0POOE3vv2LzNg1qN8MMyw/ms",
        "rating_img_url_small": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_small_5.png",
        "id": "pp33WfN_FoKlQKJ-38j_Ag",
        "text_excerpt": "Love this place!  I've been here twice now and each time has been a great experience.  The bartender is so nice.  When we had questions about the drinks he...",
        "user_name": "Scott M.",
        "mobile_uri": "http://mobile.yelp.com/biz/yyqwqfgn1ZmbQYNbl7s5sQ?srid=pp33WfN_FoKlQKJ-38j_Ag",
        "rating_img_url": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_5.png"
      }
    ],
    "phone": "4152550300",
    "neighborhoods": [
      {
        "name": "Hayes Valley",
        "url": "http://yelp.com/search?find_loc=Hayes+Valley%2C+San+Francisco%2C+CA"
      }
    ],
    "rating_img_url": "http://static.px.yelp.com/static/20070816/i/ico/stars/stars_4.png",
    "longitude": -122.429926,
    "categories": [
      {
        "name": "Dance Clubs",
        "category_filter": "danceclubs",
        "search_url": "http://yelp.com/search?find_loc=466+Haight+St%2C+San+Francisco%2C+CA&cflt=danceclubs"
      },
      {
        "name": "Lounges",
        "category_filter": "lounges",
        "search_url": "http://yelp.com/search?find_loc=466+Haight+St%2C+San+Francisco%2C+CA&cflt=lounges"
      },
      {
        "name": "American (Traditional)",
        "category_filter": "tradamerican",
        "search_url": "http://yelp.com/search?find_loc=466+Haight+St%2C+San+Francisco%2C+CA&cflt=tradamerican"
      }
    ],
    "state": "CA",
    "review_count": 32,
    "distance": 1.87804019451141
  }
]

The first thing I notice off the bat is your use of 我注意到的第一件事就是您对

response['businesses'].length.times do |i|
  # the business you want is response['businesses'][i]
end

for iterating. 进行迭代。 This can be simplified greatly by using Array.each which provides you with: 使用Array.each可以大大简化此过程,它为您提供:

response['businesses'].each do |businessEntry|
  # here, businessEntry is the actual object, so forego something like
  # response['business'][businessEntry], just use businessEntry directly
end

You're actually doing the same thing below with your: response['businesses'][businessEntry].each 您实际上在用以下方法做同样的事情:response ['businesses'] [businessEntry] .each

Note on style, it's often (though no enforced) common style do use do/end if your block is on multiple lines and {} if the block is one line. 关于样式,请注意,如果您的代码块位于多行中,则通常(尽管没有强制执行)会使用do / end;如果该代码块是一行,通常会使用{}。

Also, not sure why you're building these key/value pairs in a string, just make a hash. 另外,不确定为什么要在字符串中构建这些键/值对,只需进行哈希处理即可。 Then you can easily convert to json, or as Jorg pointed out, just convert the whole response to json which does EXACTLY what you're doing manually... unless you need to work with the data first (which it doesn't look like you need to do) 然后,您可以轻松地转换为json,或者如Jorg所指出的那样,只需将整个响应转换为json即可,它确实可以完成您的手动操作...除非您需要先使用数据(看起来像这样)你需要做)

I would be interested to see the error you were getting from hash.to_json to see if the cause for that can be found. 我很想知道您从hash.to_json得到的错误,以查看是否可以找到导致该错误的原因。

In terms of your Ruby code a couple of observations: 就您的Ruby代码而言,有以下几点观察:

  • The use of .length.times do .. is a bit odd when you could just use each eg response['businesses'].each do .. .length.times do ..的使用有些奇怪,当您仅使用each .length.times时,例如response['businesses'].each do ..

  • In your else case text+="\\"#{arrayKey}\\":\\"#{arrayValue}\\"," it looks like arrayKey and arrayValue are out of scope because they are only used as the block variables in the each above. 在您的else情况下, text+="\\"#{arrayKey}\\":\\"#{arrayValue}\\","看起来arrayKeyarrayValue不在范围内,因为它们仅用作上述each的块变量。

  • text ="" sets text back to empty string at each iteration of the outer look so as the code stands it looks like the text built up by the previous iterations of the loop is discarded. text =""在每次外观迭代时将文本重新设置为空字符串,以使代码保持原样,看起来就像丢弃了循环的先前迭代所建立的文本。

I'm no expert on Ruby, but I do know what things that if they appear in my code, my professor yells at me for. 我不是Ruby方面的专家,但是我确实知道,如果它们出现在我的代码中,我的教授会大喊大叫。 Mikej already hit on the major things, especially with the use of #each instead of #length and #times . Mikej已经涉足主要领域,尤其是使用#each代替#length#times

If I am ever iterating through a collection of some sort, the only time I ever use something other than #each is when I need to use a custom iterator, and even then you could still use #each , but you would have to have a control statement inside the passed block to make sure that the block does not execute on certain instances. 如果我要遍历某种集合,那么我唯一一次使用#each以外的东西的时候就是我需要使用自定义迭代器,即使这样,您仍然可以使用#each ,但是您必须使用传递的块内的控制语句,以确保该块不在某些实例上执行。 Even if it gets too complicated for that, the only other method of custom iteration I really use is a for i in Range.new( begin, end, optional_exclusion ) statement. 即使为此变得太复杂,我真正使用的唯一的自定义迭代方法是for i in Range.new( begin, end, optional_exclusion )语句中的for i in Range.new( begin, end, optional_exclusion ) And this could still be turned into a conditional in a block for #each , but it saves me code sometimes and makes it more explicit that I am intentionally not executing code on all elements of a collection as well as explicitly showing that I am setting the boundaries of the affected elements at the time of entry into the loop instead of hard-coded values or just the entire collection. 而且这仍然可以变成#each的条件块,但有时可以为我节省代码,并更加明确地表明我有意不在集合的所有元素上执行代码,并明确表明我正在设置进入循环时受影响元素的边界,而不是硬编码的值或整个集合。

Mikej already pointed out the out the error of scope when calls to arrayKey and arrayValue are made in the else part of the if statement so I won't bother with that. Mikej已经指出了在if语句的else部分中对arrayKey和arrayValue进行调用时范围的错误,因此我不会为之烦恼。 He also already pointed out, you should probably move your text ="" code line up by two lines to get out of that response code block scope. 他还已经指出,您可能应该将text =""代码行向上移动两行,以退出该响应代码块范围。

My only concern after that is some problems not with the code itself, but more sort of coding style and things generally practiced in the Ruby community. 在那之后,我唯一关心的是一些问题,而不是代码本身,而是更多的编码样式和Ruby社区中通常实践的事情。 So these suggestions, by no means, you have to take. 因此,这些建议绝不是您必须接受的。 It just makes reading Ruby code generally easier. 它通常使阅读Ruby代码更加容易。

So my first suggestion is whenever you call a method you are passing a block to, unless you can fit the proper indentation, object call, method call, block variable declaration, the code block, and the block closing all on the same line, then don't use curly braces. 因此,我的第一个建议是,每当您调用要传递一个块的方法时,除非您可以将适当的缩进,对象调用,方法调用,块变量声明,代码块以及关闭所有块的代码放在同一行,然后不要使用花括号。 In those situations of multi-line code blocks, use the keywords do and end instead. 在多行代码块的情况下,请使用关键字doend代替。

My second suggestion is to use proper indentation which in the Ruby language is two spaces instead of the normal four found in the coding styles of many other languages. 我的第二个建议是使用适当的缩进,该缩进在Ruby语言中是两个空格,而不是许多其他语言的编码样式中通常的四个空格。 You had proper indentation for a good chuck of your code and then you messed up and it kind caused some lines down the road to look like they are on a scope level that they are not. 您已经对代码进行了适当的缩进,然后弄糟了,这会导致沿途有些线看起来好像它们不在作用域级别上。 Now, this tip is nowhere near a coding style standard, but my general trick to make sure I am on the right scope level is just adding a end-line comment right after the use of the end keyword and put in the name of the scope it just closed. 现在,此技巧远不及编码样式标准,但我为确保自己处于正确的作用域级别而做的一般技巧是在使用end关键字后立即添加一个结束行注释,并输入作用域的名称它刚刚关闭。 It has saved me from a number of scope bugs but I have never heard of anyone else doing it and it can possibly clutter code with these random end-line comments but it's a method that has served me well. 它使我摆脱了许多范围内的错误,但我从未听说过其他人这样做,它可能会用这些随机的结束语注释使代码混乱,但这是一种很好的方法。

My third suggestion is to improve your use of strings and symbols. 我的第三个建议是改善对字符串和符号的使用。 I am almost afraid to say this just because I still need to improve my understanding of symbols in Ruby and I don't recall using the Yelp class in any recent scripts so I am blind on that one. 我几乎不敢这么说,只是因为我仍然需要提高对Ruby中符号的理解,并且我不记得在最近的任何脚本中都使用Yelp类,因此我对此一无所知。 However, it looks like you use the string 'businesses' like a Hash key. 但是,好像您将字符串'businesses'用作哈希键一样。 A general rule in Ruby is if you are using a string just for what it represents, then you should be using a symbol while if you are using a string because you actually need its character contents, then you should probably stick to using a string. Ruby中的一个一般规则是,如果您仅使用字符串来表示它所代表的含义,那么您应该使用符号,而如果您使用字符串,因为您实际上需要它的字符内容,那么您可能应该坚持使用字符串。 This is just because every time you call a string literal, a new string is made and stored in system memory. 这是因为每次您调用字符串文字时,都会创建一个新字符串并将其存储在系统内存中。 So here since you are using 'businesses' inside an each block that is inside and each block, you are potentially allocating that string O(n²) times (although the allocations for the inner block get garbage collected during the next iteration. Whereas if you used a symbol like ':businesses' instead, it only initializes it once. Also in the case of you text ="" line, you should modify that into a single quotation string literal. The Ruby interpreter can parse through single quote string literals faster than double quotes, so generally if you can use a single quoted string literal, do it. 因此,在这里,由于您在每个内部块和每个块内使用'businesses' ,因此有可能分配该字符串O(n²)次(尽管内部块的分配在下一次迭代期间会被垃圾回收。)使用了':businesses'这样的符号,它只会初始化一次。同样,如果您输入text =""行,则应将其修改为单引号字符串文字。Ruby解释器可以更快地解析单引号字符串文字而不是双引号,因此通常可以使用单引号的字符串文字。

All I got for suggestions. 我得到的都是建议。 Take them as you need them or want them. 根据需要或需要使用它们。 Also here would be what your code would look like assuming you took my suggestions and I didn't break anything in the process. 同样,假设您接受了我的建议并且在此过程中我没有破坏任何内容,那么您的代码也将是这样。

def mineLocation

  client = Yelp::Client.new
  request = Yelp::Review::Request::GeoPoint.new(:latitude=>13.3125,
                                                :longitude => -6.2468,
                                                :yws_id => 'nicetry')
  response = client.search(request)
  text = ''
  response[:businesses].each do |businessEntry|
    response[:businesses][businessEntry].each do |key, value|
      if value.kindOf( Array )
        value.each do |arrayEntry|
          text += "\"#{key}\":["
          value[arrayEntry].each do |arrayKey, arrayValue|
            text += "{\"#{arrayKey}\":\"#{arrayValue}\"},"
          end #each
          text += ']'   
        end #each
      else
        # Didn't fix because I didn't know you intentions here.
        text += "\"#{arrayKey}\":\"#{arrayValue}\"," 
      end #if
    end #each
  end #each

end #def

I didn't replace 'nicetry' with a symbol just because I don't know how the Yelp class works and might explicitly need a string there instead of a symbol. 我没有用符号代替'nicetry'只是因为我不知道Yelp类是如何工作的,可能在那里显式地需要一个字符串而不是一个符号。 I also didn't know what the intended code effect was since the only time this code is executed is when the variables are out of scope, so I had no way of knowing what you were trying to reference in that line. 我也不知道预期的代码效果是什么,因为只有在变量超出范围时才执行此代码,所以我无法知道您在该行中尝试引用的内容。 Especially since at that point your value is also not an array either. 特别是因为那时您的值也不是数组。

I know this is a long answer but I hope some of this helps! 我知道这是一个很长的答案,但我希望其中一些帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM