হ্যাশ ডিফল্ট মান, যেমন হ্যাশ.নউ ([])


107

এই কোডটি বিবেচনা করুন:

h = Hash.new(0)  # New hash pairs will by default have 0 as values
h[1] += 1  #=> {1=>1}
h[2] += 2  #=> {2=>2}

সব ঠিক আছে, কিন্তু:

h = Hash.new([])  # Empty array as default value
h[1] <<= 1  #=> {1=>[1]}                  ← Ok
h[2] <<= 2  #=> {1=>[1,2], 2=>[1,2]}      ← Why did `1` change?
h[3] << 3   #=> {1=>[1,2,3], 2=>[1,2,3]}  ← Where is `3`?

এই মুহুর্তে আমি হ্যাশটি আশা করি:

{1=>[1], 2=>[2], 3=>[3]}

তবে এটি এ থেকে অনেক দূরে। কী হচ্ছে এবং আমি যে আচরণটি প্রত্যাশা করি তা কীভাবে পেতে পারি?

উত্তর:


164

প্রথম, নোট করুন যে এই আচরণটি কেবলমাত্র অ্যারে নয়, পরিবর্তিত (যেমন হ্যাশ এবং স্ট্রিংগুলি) কোনও ডিফল্ট মানের ক্ষেত্রে প্রযোজ্য।

টিএল; ডিআর : Hash.new { |h, k| h[k] = [] }আপনি সর্বাধিক প্রতিমা সমাধান চাইলে এবং কেন এটি যত্ন করবেন না তা ব্যবহার করুন।


কি কাজ করে না

কেন Hash.new([])কাজ করে না

আসুন কেন Hash.new([])কাজ করে না তা আরও গভীরতার সাথে দেখি :

h = Hash.new([])
h[0] << 'a'  #=> ["a"]
h[1] << 'b'  #=> ["a", "b"]
h[1]         #=> ["a", "b"]

h[0].object_id == h[1].object_id  #=> true
h  #=> {}

আমরা দেখতে পাচ্ছি যে আমাদের ডিফল্ট অবজেক্টটি পুনঃব্যবহৃত এবং রূপান্তরিত হচ্ছে (এটি কারণ এটি একমাত্র এবং একমাত্র পূর্বনির্ধারিত মান হিসাবে উত্তীর্ণ হয়েছে, হ্যাশের একটি নতুন, নতুন ডিফল্ট মান পাওয়ার কোনও উপায় নেই), তবে কী বা কী মান নেই? অ্যারে, h[1]তবুও আমাদের একটি মান দেওয়া সত্ত্বেও ? এখানে একটি ইঙ্গিত দেওয়া হয়েছে:

h[42]  #=> ["a", "b"]

প্রতিটি []কল দ্বারা ফিরে আসা অ্যারে হ'ল কেবলমাত্র ডিফল্ট মান, যা আমরা এই সময়টি পরিবর্তিত করে চলেছি তাই এখন আমাদের নতুন মান রয়েছে। যেহেতু <<হ্যাশ নির্ধারিত না (সেখানে কখনোই ছাড়া রুবি মধ্যে নিয়োগ হতে পারে =বর্তমান ), আমরা কখনোই আমাদের প্রকৃত হ্যাশ মধ্যে কিছু রেখেছি। পরিবর্তে আমাদের ব্যবহার করতে হবে <<=(যা <<হিসাবে +=রয়েছে +):

h[2] <<= 'c'  #=> ["a", "b", "c"]
h             #=> {2=>["a", "b", "c"]}

এটি একই হিসাবে:

h[2] = (h[2] << 'c')

কেন Hash.new { [] }কাজ করে না

ব্যবহার Hash.new { [] }সমাধান পুনঃব্যবহার এবং মূল ডিফল্ট মান mutating (হিসেবে দেওয়া ব্লক প্রতিটি সময় বলা হয়, একটি নতুন অ্যারে ফিরে) সমস্যা, কিন্তু না নিয়োগ সমস্যা:

h = Hash.new { [] }
h[0] << 'a'   #=> ["a"]
h[1] <<= 'b'  #=> ["b"]
h             #=> {1=>["b"]}

কি কাজ করে

অ্যাসাইনমেন্টের উপায়

আমরা যদি সর্বদা ব্যবহারের কথা মনে করি <<=, তবে Hash.new { [] } এটি একটি কার্যকর সমাধান, তবে এটি কিছুটা বিজোড় এবং অহঙ্কারী (আমি বুনোতে কখনও দেখিনি <<=)। যদি <<অজান্তে ব্যবহার করা হয় তবে এটি সূক্ষ্ম বাগের প্রবণও।

পরিবর্তনীয় উপায়

ডকুমেন্টেশনHash.new রাজ্যের (জোর আমার নিজের):

যদি কোনও ব্লক নির্দিষ্ট করা থাকে, তবে এটি হ্যাশ অবজেক্ট এবং কী দ্বারা কল করা হবে এবং এটি ডিফল্ট মানটি ফিরিয়ে আনবে। যদি প্রয়োজন হয় তবে হ্যাশে মান সংরক্ষণ করা ব্লকের দায়িত্ব

সুতরাং আমরা অবশ্যই পরিবর্তে হ্যাশটিতে ডিফল্ট মানটি সংরক্ষণ করতে চাই যদি আমরা এর <<পরিবর্তে ব্যবহার করতে চাই <<=:

h = Hash.new { |h, k| h[k] = [] }
h[0] << 'a'  #=> ["a"]
h[1] << 'b'  #=> ["b"]
h            #=> {0=>["a"], 1=>["b"]}

এটি কার্যকরভাবে আমাদের পৃথক কলগুলি (যা ব্যবহার করবে <<=) থেকে পাস করা ব্লকে অ্যাসাইনমেন্টটি স্থানান্তরিত করে Hash.new, ব্যবহার করার সময় অপ্রত্যাশিত আচরণের বোঝা সরিয়ে দেয় <<

মনে রাখবেন যে এই পদ্ধতি এবং অন্যদের মধ্যে একটি কার্যকরী পার্থক্য রয়েছে: এইভাবে পড়ার সময় ডিফল্ট মান নির্ধারণ করে (যেমন অ্যাসাইনমেন্টটি সর্বদা ব্লকের অভ্যন্তরে ঘটে থাকে)। উদাহরণ স্বরূপ:

h1 = Hash.new { |h, k| h[k] = [] }
h1[:x]
h1  #=> {:x=>[]}

h2 = Hash.new { [] }
h2[:x]
h2  #=> {}

অপরিবর্তনীয় উপায়

আপনি ভাবতে পারেন যে ঠিক কাজ Hash.new([])করার সময় কেন কাজ করে না Hash.new(0)। মূলটি হ'ল রুবিতে সংখ্যাসূচকগুলি অপরিবর্তনীয়, তাই আমরা স্বাভাবিকভাবেই কখনই সেগুলিকে জায়গায় স্থান দিতে পারি না। যদি আমরা আমাদের ডিফল্ট মানটিকে অপরিবর্তনীয় হিসাবে বিবেচনা করি, আমরা Hash.new([])কেবল জরিমানাও ব্যবহার করতে পারি :

h = Hash.new([].freeze)
h[0] += ['a']  #=> ["a"]
h[1] += ['b']  #=> ["b"]
h[2]           #=> []
h              #=> {0=>["a"], 1=>["b"]}

তবে, এটি নোট করুন ([].freeze + [].freeze).frozen? == false। সুতরাং, আপনি যদি নিশ্চিত করতে চান যে অপরিবর্তনশীলতা জুড়ে রক্ষা করা হয়েছে, তবে আপনাকে অবশ্যই নতুন অবজেক্টটি পুনরায় জমা করার জন্য যত্ন নিতে হবে।


উপসংহার

সমস্ত উপায়গুলির মধ্যে আমি ব্যক্তিগতভাবে "অপরিবর্তনীয় উপায়" পছন্দ করি — অনিবার্যতা সাধারণত বিষয়গুলি সম্পর্কে যুক্তি অনেক সহজ করে তোলে। সর্বোপরি, এটি একমাত্র পদ্ধতি যা লুকানো বা সূক্ষ্ম অপ্রত্যাশিত আচরণের কোনও সম্ভাবনা রাখে না। তবে, সর্বাধিক প্রচলিত এবং মূর্খতাপূর্ণ উপায় হ'ল "পরিবর্তনীয় উপায়"।

চূড়ান্ত হিসাবে, হ্যাশ ডিফল্ট মানগুলির এই আচরণটি রুবি কোয়ানসে লক্ষ্য করা যায় ।


এটি কঠোরভাবে সত্য নয়, instance_variable_setএটিকে বাইপাস করার মতো পদ্ধতিগুলি , তবে তাদের রূপান্তরকাজের জন্য অবশ্যই উপস্থিত থাকতে হবে কারণ এল-মানটি =গতিশীল হতে পারে না।


1
এটি উল্লেখ করে যে "মিউটটেবল ওয়ে" ব্যবহারের ফলে প্রতিটি হ্যাশ লুককে একটি মূল মান জুড়ি (যেহেতু ব্লকের মধ্যে একটি অ্যাসাইনমেন্ট রয়েছে সেখানে) সংরক্ষণ করতে পারে, যা সর্বদা পছন্দসই নাও হতে পারে।
জনসিপ

@ জোহানসিপ প্রতিটি দর্শন নয় , প্রতিটি কী-তে প্রথম প্রথম। তবে আমি আপনার অর্থটি দেখতে পাচ্ছি, আমি উত্তরটি পরে যুক্ত করব; ধন্যবাদ !.
অ্যান্ড্রু মার্শাল

ওফফ, ম্লান হচ্ছে। আপনি ঠিক বলেছেন, অবশ্যই এটি কোনও অজানা কীটির প্রথম অনুসন্ধান। আমি প্রায় মত মনে { [] }সঙ্গে <<=অল্পসংখ্যক পরিষেবা চমকের আছে, এটা সত্য যে ঘটনাক্রমে বিস্মরণ না থাকত =একটি খুব বিভ্রান্তিকর ডিবাগ অধিবেশন হতে পারে।
জনসিপ

ডিফল্ট মানগুলির সাথে হ্যাশ শুরু করার সময় পার্থক্য সম্পর্কে বেশ স্পষ্ট ব্যাখ্যা
সিসোলারিক্স

23

আপনি উল্লেখ করছেন যে হ্যাশের জন্য ডিফল্ট মানটি সেই নির্দিষ্ট (প্রাথমিকভাবে খালি) অ্যারের একটি রেফারেন্স।

আমি মনে করি আপনি চান:

h = Hash.new { |hash, key| hash[key] = []; }
h[1]<<=1 
h[2]<<=2 

এটি প্রতিটি কীটির জন্য একটি নতুন অ্যারেতে ডিফল্ট মান সেট করে ।


প্রতিটি নতুন হ্যাশের জন্য আমি কীভাবে পৃথক অ্যারে উদাহরণ ব্যবহার করতে পারি?
ভ্যালেন্টিন ভ্যাসিলিভ

5
সেই ব্লক সংস্করণ আপনাকে Arrayপ্রতিটি অনুরোধে নতুন দৃষ্টান্ত দেয় । জানাচ্ছি: h = Hash.new { |hash, key| hash[key] = []; puts hash[key].object_id }; h[1] # => 16348490; h[2] # => 16346570। এছাড়াও: আপনি যদি ব্লক সংস্করণটি ব্যবহার করেন যা মান ( ) কেবলমাত্র মান ( ) উত্পন্ন করার পরিবর্তে মান ( ) সেট করে , তবে আপনার কেবলমাত্র প্রয়োজন , উপাদান যুক্ত করার সময় নয় । {|hash,key| hash[key] = []}{ [] }<<<<=
জেমস এ রোজেন

3

অপারেটরগুলি +=যখন সেই হ্যাশগুলিতে প্রয়োগ করা হয় তখন প্রত্যাশা অনুযায়ী কাজ করে।

[1] pry(main)> foo = Hash.new( [] )
=> {}
[2] pry(main)> foo[1]+=[1]
=> [1]
[3] pry(main)> foo[2]+=[2]
=> [2]
[4] pry(main)> foo
=> {1=>[1], 2=>[2]}
[5] pry(main)> bar = Hash.new { [] }
=> {}
[6] pry(main)> bar[1]+=[1]
=> [1]
[7] pry(main)> bar[2]+=[2]
=> [2]
[8] pry(main)> bar
=> {1=>[1], 2=>[2]}

এটি হতে পারে কারণ ডানদিকে যখন ডানদিকে মূল্যায়ন করা হয় তখন এটি foo[bar]+=bazসিনট্যাকটিক চিনির জন্য এটি ডিফল্ট মান বস্তুটি ফেরত দেয় এবং অপারেটর এটি পরিবর্তন করে না। বাম হাতটি পদ্ধতির জন্য সিনট্যাকটিক চিনি যা ডিফল্ট মান পরিবর্তন করে না ।foo[bar]=foo[bar]+bazfoo[bar]=+[]=

নোট করুন যে এটি এর সাথে প্রযোজ্য হবে না foo[bar]<<=bazকারণ এটি ডিফল্ট মানের সমতুল্য হবে foo[bar]=foo[bar]<<bazএবং পরিবর্তিত << হবে

এছাড়াও, আমি Hash.new{[]}এবং এর মধ্যে কোনও পার্থক্য খুঁজে পাইনি Hash.new{|hash, key| hash[key]=[];}। কমপক্ষে রুবিতে 2.1.2।


সুন্দর ব্যাখ্যা। এটি রুবি ২.১.১-এর মতো প্রত্যাশিত আচরণের অভাবের সাথে আমার কাছে Hash.new{[]}একই (যদিও এটি কাজ করে)। অদ্ভুত ছোট ছোট জিনিসগুলি সমস্ত জিনিস ভেঙে: /Hash.new([])<<Hash.new{|hash, key| hash[key]=[];}
butterywombat

1

আপনি যখন লিখবেন,

h = Hash.new([])

আপনি হ্যাশের সমস্ত উপাদানগুলিতে অ্যারের ডিফল্ট রেফারেন্সটি পাস করেন। যে কারণে হ্যাশের সমস্ত উপাদান একই অ্যারে বোঝায়।

আপনি যদি হ্যাশের প্রতিটি উপাদান পৃথক অ্যারে উল্লেখ করেন তবে আপনার ব্যবহার করা উচিত

h = Hash.new{[]} 

এটি রুবিতে কীভাবে কাজ করে তার আরও বিশদ জানতে দয়া করে এটিকে দেখুন: http://ruby-doc.org/core-2.2.0/Array.html#method-c-new


এটা ভুল, কাজ Hash.new { [] }করে না । বিস্তারিত জানার জন্য আমার উত্তর দেখুন । এটি ইতিমধ্যে অন্য উত্তরের প্রস্তাবিত সমাধানও।
অ্যান্ড্রু মার্শাল
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.