স্ট্রিংটি যদি রুবেলে অন রাইলে একটি সংখ্যা হয় তবে পরীক্ষা করুন


103

আমার অ্যাপ্লিকেশন নিয়ামকটিতে আমার নিম্নলিখিত রয়েছে:

def is_number?(object)
  true if Float(object) rescue false
end

এবং আমার নিয়ামকটিতে নিম্নলিখিত শর্ত:

if mystring.is_number?

end

শর্তটি একটি undefined methodত্রুটি ছুড়ে দিচ্ছে। আমি অনুমান করছি যে আমি is_numberভুল জায়গায় সংজ্ঞায়িত করেছি ...?


4
আমি জানি যে জম্বি পরীক্ষার ক্লাসের জন্য কোডস্কুলের রেলগুলির কারণে প্রচুর লোক এখানে রয়েছে। কেবল তার ব্যাখ্যা দিয়ে যাওয়ার জন্য অপেক্ষা করুন। পরীক্ষাগুলি উত্তীর্ণ হওয়ার কথা নয় --- আপনি পরীক্ষাটি ভুলরূপে ব্যর্থ হওয়ার জন্য ঠিক আছে, আপনি সর্বদা self.is_number এর মতো পদ্ধতি আবিষ্কার করতে রেলগুলি প্যাচ করতে পারেন?
বোল্ডার_রবি

গৃহীত উত্তর "1,000" এর মতো ক্ষেত্রে ব্যর্থ হয় এবং একটি রেজেক্স পদ্ধতির ব্যবহারের চেয়ে 39x ধীর। আমার উত্তর নীচে দেখুন।
pthamm

উত্তর:


186

is_number?পদ্ধতি তৈরি করুন ।

একটি সহায়ক পদ্ধতি তৈরি করুন:

def is_number? string
  true if Float(string) rescue false
end

এবং তারপরে এটিকে কল করুন:

my_string = '12.34'

is_number?( my_string )
# => true

Stringক্লাস প্রসারিত করুন ।

আপনি যদি is_number?আপনার সহায়ক ফাংশনে পারম হিসাবে পাস করার পরিবর্তে স্ট্রিংয়ে সরাসরি কল করতে সক্ষম হতে চান তবে আপনাকে ক্লাসের is_number?এক্সটেনশন হিসাবে সংজ্ঞায়িত করতে হবে String:

class String
  def is_number?
    true if Float(self) rescue false
  end
end

এবং তারপরে আপনি এটিকে কল করতে পারেন:

my_string.is_number?
# => true

2
এটি একটি খারাপ ধারণা। "330.346.11"। টো_ফ # => 330.346
মহাকাব্য

11
সেখানে নেই to_f: উপরে, এবং ফ্লোট () প্রদর্শন নয় যে আচরণ Float("330.346.11")উত্থাপনArgumentError: invalid value for Float(): "330.346.11"
জ্যাকব এস

7
আপনি যদি এই প্যাচটি ব্যবহার করেন তবে আমি রুবি নামকরণ কনভেনশনগুলির সাথে তাল মিলিয়ে নাম্বারের নাম দিয়ে নামকরণ করব?
কনরাড রেইচে

10
মূল প্রশ্নের সাথে সত্যই প্রাসঙ্গিক নয়, তবে আমি সম্ভবত কোডটি ভিতরে রেখেছি lib/core_ext/string.rb
Jakob এস

1
আমি মনে করি না is_number?(string)বিটটি রুবি ১.৯ এর কাজ করে। হতে পারে এটি কারাগারের অংশ বা 1.8? String.is_a?(Numeric)কাজ করে। স্ট্যাকওভারফ্লো . com/ প্রশ্নগুলি / ২০৯৯৯৯৩/২ দেখুন ।
রস অ্যাট্রিল

30

এই সমস্যা সমাধানের সাধারণ উপায়গুলির জন্য এখানে একটি মানদণ্ড। কোনটি আপনার সম্ভবত ব্যবহার করা উচিত তা প্রত্যাশিত মিথ্যা মামলার অনুপাতের উপর নির্ভর করে Note

  1. যদি তারা তুলনামূলকভাবে অস্বাভাবিক কাস্টিং হয় তবে অবশ্যই তা দ্রুত।
  2. যদি মিথ্যা মামলাগুলি সাধারণ হয় এবং আপনি কেবল কালি অনুসন্ধান করছেন, তুলনামূলক বনাম একটি রূপান্তরিত রাষ্ট্র একটি ভাল বিকল্প।
  3. যদি মিথ্যা মামলাগুলি সাধারণ হয় এবং আপনি ভাসমানগুলি পরীক্ষা করে দেখেন তবে সম্ভবত রেজিএক্সপেক্টই যাওয়ার উপায়

যদি পারফরম্যান্সে আপনার পছন্দের জিনিসটি ব্যবহার না করে। :-)

পূর্ণসংখ্যার পরীক্ষার বিশদ:

# 1.9.3-p448
#
# Calculating -------------------------------------
#                 cast     57485 i/100ms
#            cast fail      5549 i/100ms
#                 to_s     47509 i/100ms
#            to_s fail     50573 i/100ms
#               regexp     45187 i/100ms
#          regexp fail     42566 i/100ms
# -------------------------------------------------
#                 cast  2353703.4 (±4.9%) i/s -   11726940 in   4.998270s
#            cast fail    65590.2 (±4.6%) i/s -     327391 in   5.003511s
#                 to_s  1420892.0 (±6.8%) i/s -    7078841 in   5.011462s
#            to_s fail  1717948.8 (±6.0%) i/s -    8546837 in   4.998672s
#               regexp  1525729.9 (±7.0%) i/s -    7591416 in   5.007105s
#          regexp fail  1154461.1 (±5.5%) i/s -    5788976 in   5.035311s

require 'benchmark/ips'

int = '220000'
bad_int = '22.to.2'

Benchmark.ips do |x|
  x.report('cast') do
    Integer(int) rescue false
  end

  x.report('cast fail') do
    Integer(bad_int) rescue false
  end

  x.report('to_s') do
    int.to_i.to_s == int
  end

  x.report('to_s fail') do
    bad_int.to_i.to_s == bad_int
  end

  x.report('regexp') do
    int =~ /^\d+$/
  end

  x.report('regexp fail') do
    bad_int =~ /^\d+$/
  end
end

ফ্লোট পরীক্ষার বিশদ:

# 1.9.3-p448
#
# Calculating -------------------------------------
#                 cast     47430 i/100ms
#            cast fail      5023 i/100ms
#                 to_s     27435 i/100ms
#            to_s fail     29609 i/100ms
#               regexp     37620 i/100ms
#          regexp fail     32557 i/100ms
# -------------------------------------------------
#                 cast  2283762.5 (±6.8%) i/s -   11383200 in   5.012934s
#            cast fail    63108.8 (±6.7%) i/s -     316449 in   5.038518s
#                 to_s   593069.3 (±8.8%) i/s -    2962980 in   5.042459s
#            to_s fail   857217.1 (±10.0%) i/s -    4263696 in   5.033024s
#               regexp  1383194.8 (±6.7%) i/s -    6884460 in   5.008275s
#          regexp fail   723390.2 (±5.8%) i/s -    3613827 in   5.016494s

require 'benchmark/ips'

float = '12.2312'
bad_float = '22.to.2'

Benchmark.ips do |x|
  x.report('cast') do
    Float(float) rescue false
  end

  x.report('cast fail') do
    Float(bad_float) rescue false
  end

  x.report('to_s') do
    float.to_f.to_s == float
  end

  x.report('to_s fail') do
    bad_float.to_f.to_s == bad_float
  end

  x.report('regexp') do
    float =~ /^[-+]?[0-9]*\.?[0-9]+$/
  end

  x.report('regexp fail') do
    bad_float =~ /^[-+]?[0-9]*\.?[0-9]+$/
  end
end

29
class String
  def numeric?
    return true if self =~ /\A\d+\Z/
    true if Float(self) rescue false
  end
end  

p "1".numeric?  # => true
p "1.2".numeric? # => true
p "5.4e-29".numeric? # => true
p "12e20".numeric? # true
p "1a".numeric? # => false
p "1.2.3.4".numeric? # => false

12
/^\d+$/রুবির কোনও নিরাপদ রেজিপ্সপ নয় /\A\d+\Z/। (যেমন "42 \ n কিছু পাঠ্য" ফিরে আসবে true)
টিমোথি এ

@ টিমোথিয়ার মন্তব্য সম্পর্কে স্পষ্ট করতে, /^\d+$/লাইনগুলি নিয়ে কাজ করা থাকলে এটি ব্যবহার করা নিরাপদ তবে এই ক্ষেত্রে এটি একটি স্ট্রিংয়ের শুরু এবং সমাপ্তি সম্পর্কে /\A\d+\Z/
জুলিও

1
উত্তরগুলি উত্তর দিয়ে সত্যিকারের উত্তর পরিবর্তন করতে সম্পাদনা করা উচিত নয়? উত্তরটি সম্পাদনাতে পরিবর্তন করা আপনি যদি উত্তর না করেন তবে মনে হয় ... সম্ভবত হস্তান্তরিত এবং সীমার বাইরে থাকা উচিত।
জায়েদেল

2
\ Z স্ট্রিংয়ের শেষে \ n রাখার অনুমতি দেয়, সুতরাং এটি "123" n "পুরোপুরি সংখ্যাসূচক না হলেও বৈধতা যাচাই করবে। তবে আপনি যদি \ z ব্যবহার করেন তবে এটি আরও সঠিক হবে regexp: / \ A \ d + \ z /
সানিমাগদান

15

উত্থাপিত ব্যতিক্রমের উপর নির্ভর করা দ্রুত, পঠনযোগ্য বা নির্ভরযোগ্য সমাধান নয়।
আমি নিম্নলিখিতটি করতাম:

my_string.should =~ /^[0-9]+$/

1
এটি কেবল ইতিবাচক পূর্ণসংখ্যার জন্য কাজ করে। '-1', '0.0', বা '1_000' এর মতো মানগুলি বৈধ সংখ্যাসূচক মান সত্ত্বেও সমস্ত মিথ্যা প্রত্যাবর্তন করে। আপনি / ^ [- .0-9] + $ / এর মতো কিছু খুঁজছেন , তবে এটি ভুলভাবে '- -' গ্রহণ করে
জাকব এস

13
রেলগুলি 'বৈধতা_সংখ্যকতা_সামান্য' থেকে: কাঁচা_মূল্য.টো_স = ~ / \ এ [+ -]? \ D + \ জেড /
মর্টেন

NoMethodError: অপরিজ্ঞাত পদ্ধতি asd উচিত "সংযুক্ত" জন্য: স্ট্রিং
সার্গের্গ

সর্বশেষ rspec, এই হয়েexpect(my_string).to match(/^[0-9]+$/)
ডেমিয়েন Mathieu

আমি পছন্দ করি: my_string =~ /\A-?(\d+)?\.?\d+\Z/এটি আপনাকে '.1', '-0.1', বা '12' করতে দেয় তবে '' বা '-' বা 'করতে দেয়।
জোশ

8

রুবি ২.6.০ অনুসারে, সংখ্যাসূচক cast exceptionালাইয়ের পদ্ধতিগুলির একটি -চ্ছিক বিভাগ রয়েছে [১] । এটি আমাদের নিয়ন্ত্রণ প্রবাহ হিসাবে ব্যতিক্রমগুলি ব্যবহার না করে বিল্ট-ইন পদ্ধতিগুলি ব্যবহার করতে সক্ষম করে:

Float('x') # => ArgumentError (invalid value for Float(): "x")
Float('x', exception: false) # => nil

অতএব, আপনাকে নিজের পদ্ধতি নির্ধারণ করতে হবে না, তবে সরাসরি যেমন ভেরিয়েবলগুলি পরীক্ষা করতে পারে

if Float(my_var, exception: false)
  # do something if my_var is a float
end

7

এইভাবে আমি এটি করি তবে আমার মনে হয় আরও ভাল উপায় থাকতে হবে

object.to_i.to_s == object || object.to_f.to_s == object

5
এটি ভাসমান স্বরলিপিটি সনাক্ত করে না, যেমন 1.2e + 35।
hipertracker

1
রুবি ২.৪.০ এ আমি দৌড়েছি object = "1.2e+35"; object.to_f.to_s == objectএবং এটি কাজ করেছে
জিওভান্নি বেনুসি

6

না আপনি কেবল এটি ভুল ব্যবহার করছেন। তোমার নাম্বার? একটি যুক্তি আছে। আপনি এটিকে যুক্তি ছাড়াই বলেছিলেন

আপনার কি করা উচিত_সংখ্যা? (মাইস্ট্রিং)


Is_number এর উপর ভিত্তি করে? প্রশ্নে পদ্ধতি, is_a ব্যবহার করে? সঠিক উত্তর দিচ্ছে না। যদি mystringসত্যিই একটি স্ট্রিং হয় তবে mystring.is_a?(Integer)সর্বদা মিথ্যা হবে। দেখে মনে হচ্ছে তিনি এর মতো একটি ফলাফল চানis_number?("12.4") #=> true
Jakob S

জাকব এস সঠিক। মাইস্ট্রিং প্রকৃতপক্ষে একটি স্ট্রিং, তবে এটি কেবল সংখ্যার সমন্বয়ে থাকতে পারে। আমার প্রশ্নটি সম্ভবত_সংখ্যক হওয়া উচিত ছিল? যাতে ডেটাটাইপটি বিভ্রান্ত না হওয়ার জন্য
জেমি বুচানান

6

Tl; dr: একটি রেজেক্স পদ্ধতির ব্যবহার করুন। এটি গ্রহণযোগ্য উত্তরে উদ্ধার পদ্ধতির চেয়ে 39x গতিযুক্ত এবং "1000" এর মতো মামলাগুলি পরিচালনা করে

def regex_is_number? string
  no_commas =  string.gsub(',', '')
  matches = no_commas.match(/-?\d+(?:\.\d+)?/)
  if !matches.nil? && matches.size == 1 && matches[0] == no_commas
    true
  else
    false
  end
end

-

@ জ্যাকব এস-এর গৃহীত উত্তর বেশিরভাগ অংশের জন্য কাজ করে, তবে ব্যতিক্রম ধরা সত্যিই ধীর হতে পারে। এছাড়াও, "1000" এর মতো স্ট্রিংয়ে উদ্ধার পদ্ধতির ব্যর্থতা রয়েছে।

আসুন পদ্ধতিগুলি সংজ্ঞা দিন:

def rescue_is_number? string
  true if Float(string) rescue false
end

def regex_is_number? string
  no_commas =  string.gsub(',', '')
  matches = no_commas.match(/-?\d+(?:\.\d+)?/)
  if !matches.nil? && matches.size == 1 && matches[0] == no_commas
    true
  else
    false
  end
end

এবং এখন কিছু পরীক্ষার কেস:

test_cases = {
  true => ["5.5", "23", "-123", "1,234,123"],
  false => ["hello", "99designs", "(123)456-7890"]
}

এবং পরীক্ষার কেসগুলি চালানোর জন্য একটি ছোট কোড:

test_cases.each do |expected_answer, cases|
  cases.each do |test_case|
    if rescue_is_number?(test_case) != expected_answer
      puts "**rescue_is_number? got #{test_case} wrong**"
    else
      puts "rescue_is_number? got #{test_case} right"
    end

    if regex_is_number?(test_case) != expected_answer
      puts "**regex_is_number? got #{test_case} wrong**"
    else
      puts "regex_is_number? got #{test_case} right"
    end  
  end
end

এখানে পরীক্ষার কেসগুলির আউটপুট দেওয়া হচ্ছে:

rescue_is_number? got 5.5 right
regex_is_number? got 5.5 right
rescue_is_number? got 23 right
regex_is_number? got 23 right
rescue_is_number? got -123 right
regex_is_number? got -123 right
**rescue_is_number? got 1,234,123 wrong**
regex_is_number? got 1,234,123 right
rescue_is_number? got hello right
regex_is_number? got hello right
rescue_is_number? got 99designs right
regex_is_number? got 99designs right
rescue_is_number? got (123)456-7890 right
regex_is_number? got (123)456-7890 right

কিছু কর্মক্ষমতা মানদণ্ড করার সময়:

Benchmark.ips do |x|

  x.report("rescue") { test_cases.values.flatten.each { |c| rescue_is_number? c } }
  x.report("regex") { test_cases.values.flatten.each { |c| regex_is_number? c } }

  x.compare!
end

এবং ফলাফল:

Calculating -------------------------------------
              rescue   128.000  i/100ms
               regex     4.649k i/100ms
-------------------------------------------------
              rescue      1.348k 16.8%) i/s -      6.656k
               regex     52.113k  7.8%) i/s -    260.344k

Comparison:
               regex:    52113.3 i/s
              rescue:     1347.5 i/s - 38.67x slower

মানদণ্ডের জন্য ধন্যবাদ। গৃহীত উত্তরের মতো ইনপুট গ্রহণ করার সুবিধা রয়েছে 5.4e-29। আমার ধারণা আপনার রেজেেক্সগুলি সেগুলি গ্রহণ করার জন্যও টুইট করা যেতে পারে।
Jodi

3
1,000 এর মতো কেসগুলি পরিচালনা করা সত্যিই শক্ত, কারণ এটি ব্যবহারকারীর উদ্দেশ্যগুলির উপর নির্ভর করে। সংখ্যার ফর্ম্যাট করার জন্য অনেকগুলি উপায় রয়েছে। 1,000 কি প্রায় 1000 এর সমান বা প্রায় 1 এর সমান? বেশিরভাগ পৃথিবী বলে যে এটি প্রায় 1, পূর্ণসংখ্যা 1000 দেখানোর উপায় নয়
জেমস মুর

4

রেল 4 এ, আপনাকে require File.expand_path('../../lib', __FILE__) + '/ext/string' আপনার কনফিগারেশন / অ্যাপ্লিকেশন.আরবি লাগাতে হবে


1
আসলে আপনার এটি করার দরকার নেই, আপনি কেবল "আরম্ভকারীগুলিতে" স্ট্রিং.আরবি স্থাপন করতে পারেন এবং এটি কাজ করে!
মহাতমানিচ

3

যদি আপনি যুক্তির অংশ হিসাবে ব্যতিক্রমগুলি ব্যবহার না করা পছন্দ করেন তবে আপনি এটি চেষ্টা করতে পারেন:

class String
   def numeric?
    !!(self =~ /^-?\d+(\.\d*)?$/)
  end
end

অথবা, যদি আপনি এটি সমস্ত বস্তু শ্রেণীর জুড়ে কাজ করতে চাই, প্রতিস্থাপন class Stringসঙ্গে class Objectএকটি স্ট্রিং একটি রূপান্তর স্ব: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)


অস্বীকার করা এবং nil?শূন্য করার উদ্দেশ্য রুবীর উপর নির্ভরযোগ্য, তাই আপনি ঠিক করতে পারেন!!(self =~ /^-?\d+(\.\d*)?$/)
আর্নল্ড রোয়া

ব্যবহার !!অবশ্যই কাজ করে। কমপক্ষে একটি রুবির স্টাইল গাইড ( github.com/bbatsov/ruby-style-guide ) পাঠযোগ্যতার !!পক্ষে এড়ানোর পরামর্শ দিয়েছিল .nil?, তবে আমি !!জনপ্রিয় সংগ্রহস্থলগুলিতে ব্যবহার করেছি এবং আমি মনে করি এটি বুলিয়ান রূপান্তরিত করার জন্য একটি ভাল উপায় way আমি উত্তর সম্পাদনা করেছি।
মার্ক স্নাইডার

-3

নিম্নলিখিত ফাংশন ব্যবহার করুন:

def is_numeric? val
    return val.try(:to_f).try(:to_s) == val
end

তাই হয়,

is_numeric? "1.2f" = মিথ্যা

is_numeric? "1.2" = সত্য

is_numeric? "12f" = মিথ্যা

is_numeric? "12" = সত্য


ভ্যাল হলে এটি ব্যর্থ হবে "0"। এছাড়াও মনে রাখবেন যে পদ্ধতিটি .tryরুবি কোর লাইব্রেরির অংশ নয় এবং কেবলমাত্র যদি আপনি অ্যাক্টিভসপোর্টটি অন্তর্ভুক্ত করেন তবে উপলব্ধ।
জিএমএ

আসলে, এটির জন্যও ব্যর্থ হয় "12", সুতরাং আপনার এই প্রশ্নের চতুর্থ উদাহরণটি ভুল। "12.10"এবং "12.00"খুব ব্যর্থ।
জিএমএ

-5

এই সমাধানটি কেমন বোবা?

def is_number?(i)
  begin
    i+0 == i
  rescue TypeError
    false
  end
end

1
এটি উপ-সর্বোত্তম কারণ '। संबंधित_টি? (: +)' ব্যবহার করা সুনির্দিষ্ট পদ্ধতিতে (: +) কলটিতে ব্যর্থতা এবং ক্যাপচারের পরে সর্বদা ভাল। এটি বিভিন্ন কারণে রেগেক্স এবং রূপান্তর পদ্ধতি না করায় বিভিন্ন কারণে ব্যর্থ হতে পারে।
স্কেয়াকি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.