كيف يمكنني تزيين هذه السلسلة في روبي؟
-
23-08-2019 - |
سؤال
لدي هذه السلسلة:
%{Children^10 Health "sanitation management"^5}
وأريد تحويله إلى اجتياز هذا في صفيف من التجزئة:
[{:keywords=>"children", :boost=>10}, {:keywords=>"health", :boost=>nil}, {:keywords=>"sanitation management", :boost=>5}]
أنا أدرك stringscanner و جنة بناء جملة ولكن لا يمكنني العثور على أمثلة كافية من التعليمات البرمجية لكليهما.
أي مؤشرات؟
المحلول
للحصول على لغة حقيقية، لعبة Lexer للذهاب - مثل جوس قال. وبعد ولكن إذا كانت اللغة الكاملة معقدة مثل مثالك، فيمكنك استخدام هذا الاختراق السريع:
irb> text = %{Children^10 Health "sanitation management"^5}
irb> text.scan(/(?:(\w+)|"((?:\\.|[^\\"])*)")(?:\^(\d+))?/).map do |word,phrase,boost|
{ :keywords => (word || phrase).downcase, :boost => (boost.nil? ? nil : boost.to_i) }
end
#=> [{:boost=>10, :keywords=>"children"}, {:boost=>nil, :keywords=>"health"}, {:boost=>5, :keywords=>"sanitation management"}]
إذا كنت تحاول تحليل لغة منتظمة، فسوف تكفي هذه الطريقة - على الرغم من أنه لن يستغرق الكثير من المضاعفات لجعل اللغة غير منتظمة.
انهيار سريع لريسكس:
\w+
يطابق أي كلمات رئيسية واحدة(?:\\.|[^\\"]])*
يستخدم الأقواس غير القبض ((?:...)
) مطابقة محتويات سلسلة مزدوجة هرب - إما رمز هارب (\n
,\"
,\\
, ، إلخ.) أو أي حرف واحد ليس رمز الهروب أو اقتباس نهاية."((?:\\.|[^\\"]])*)"
يلتقط فقط محتويات عبارة الكلمات الرئيسية المعروضة.(?:(\w+)|"((?:\\.|[^\\"])*)")
يطابق أي كلمة رئيسية - مصطلح واحد أو عبارة، والتقاط بشروط واحدة$1
ومحتويات العبارة في$2
\d+
يطابق عدد.\^(\d+)
يلتقط رقما يتبع كاريت (^
). لأن هذه هي المجموعة الثالثة من التقاط الأقواس، فسيكون ذلك$3
.(?:\^(\d+))?
يلتقط الرقم التالي لقضاء سيارة إذا كان هناك، يطابق السلسلة الفارغة خلاف ذلك.
String#scan(regex)
يطابق Regex مقابل السلسلة عدة مرات قدر الإمكان، إخراج مجموعة من "التطابقات". إذا كان Regex يحتوي على جنيات التقاط، فإن "المباراة" هي صفيف من العناصر التي تم التقاطها - هكذا $1
يصبح match[0]
, $2
يصبح match[1]
, ، إلخ. أي التقاط الأقواس التي لا تحصل على مطابقة ضد جزء من خرائط السلسلة إلى nil
الدخول في "المباراة" الناتجة.
ال #map
ثم يأخذ هذه المباريات، ويستخدم بعض السحر الكتلة لكسر كل مصطلح تم التقاطه في متغيرات مختلفة (يمكننا القيام به do |match| ; word,phrase,boost = *match
)، ثم يخلق الخلاص المرغوب الخاص بك. بالضبط واحد من word
أو phrase
سوف يكون nil
, ، بما أن كلاهما لا يمكن أن يتنابق مع المدخلات، لذلك (word || phrase)
سوف تعيد غيرnil
واحد و #downcase
سوف يحوله إلى جميع الأحرف الصغيرة. boost.to_i
سوف تحويل سلسلة إلى عدد صحيح في حين (boost.nil? ? nil : boost.to_i)
سوف تضمن أن nil
يعزز البقاء nil
.
نصائح أخرى
هنا مثال غير قوي باستخدام StringScanner
. وبعد هذا هو رمز أنا فقط تكييفها من مسابقة روبي: تحليل JSON, ، والتي لديها تفسير ممتاز.
require 'strscan'
def test_parse
text = %{Children^10 Health "sanitation management"^5}
expected = [{:keywords=>"children", :boost=>10}, {:keywords=>"health", :boost=>nil}, {:keywords=>"sanitation management", :boost=>5}]
assert_equal(expected, parse(text))
end
def parse(text)
@input = StringScanner.new(text)
output = []
while keyword = parse_string || parse_quoted_string
output << {
:keywords => keyword,
:boost => parse_boost
}
trim_space
end
output
end
def parse_string
if @input.scan(/\w+/)
@input.matched.downcase
else
nil
end
end
def parse_quoted_string
if @input.scan(/"/)
str = parse_quoted_contents
@input.scan(/"/) or raise "unclosed string"
str
else
nil
end
end
def parse_quoted_contents
@input.scan(/[^\\"]+/) and @input.matched
end
def parse_boost
if @input.scan(/\^/)
boost = @input.scan(/\d+/)
raise 'missing boost value' if boost.nil?
boost.to_i
else
nil
end
end
def trim_space
@input.scan(/\s+/)
end
ما لديك هنا هو القواعد التعسفي، وتحليلها ما تريده حقا هو Lexer - يمكنك كتابة ملف قواعد اللغة والذي وصف بناء جملة الخاص بك ثم استخدم Lexer لتوليد محلل متكرر من قواعد اللغة الخاصة بك.
كتابة Lexer (أو حتى محللا متكررا) ليس تافهة حقا - على الرغم من أنه تمرين مفيد في البرمجة - ولكن يمكنك العثور على قائمة ب Luby Lexers / المحللين في رسالة البريد الإلكتروني هذه هنا: http://newsgroups.derkeiler.com/archive/comp/comp.lang.ruby/2005-11/msg02233.html.
تتوفر RACC كوحدة قياسية ل Ruby 1.8، لذلك أقترح عليك التركيز على ذلك حتى لو كان دليله ليس من السهل اتباعه ويتطلب معرفة YACC.