Случайное изменение символов в строке в Ruby - PullRequest
1 голос
/ 30 января 2020

Я хочу случайным образом изменить регистр строки в верхний или нижний регистр. Здесь есть аналогичный вопрос, за исключением того, что он не гарантирует, что строка будет изменена.

Я сейчас использую:

def random_case_changes(string_to_change: string, change_case: 'downcase')
  raise ArgumentError, "Param is #{string_to_change.class}, not a string" unless string_to_change.is_a? String

  string_to_change.chars.map { |char| (rand 0..2).zero? ? char : char.send(change_case) }.join
end

random_case_changes(string_to_change: 'Hello', change_case: 'upcase')

Есть вероятность, что это просто вернет 'Hello.

Я смотрел на использование .sample для массива символов, но это сбивает с толку порядок, и я не нашел способ поставить верните строку в исходный порядок после внесения изменений.

Есть ли способ гарантировать, что изменение произойдет?

Ответы [ 4 ]

2 голосов
/ 30 января 2020

Есть ли способ гарантировать, что изменение произойдет?

Вы можете получить индексы символа, которые могли бы изменить, например строчные :

string_to_change = 'Hello'

indices = string_to_change.enum_for(:scan, /[[:lower:]]/).map { $~.begin(0) }
#=> [1, 2, 3, 4]

И из этого массива выберите между 1 и всеми элементами, которые изменится : (порядок не имеет значения)

indices_to_be_changed = indices.sample(rand(1..indices.size))
#=> [4, 2]

Теперь все вам нужно поменять регистр соответствующих символов: (сверху вниз или наоборот)

indices_to_be_changed.each do |i|
  string_to_change[i] = string_to_change[i].swapcase
end

string_to_change
#=> "HeLlO"
0 голосов
/ 31 января 2020

Спасибо всем за ваш вклад. Эти ответы толкнули меня в кроличью нору изучения и исследования.

Вот решение, с которым я пошел:

def random_case_change(string, options = {})
  new_case = options[:new_case].nil? ? 'upcase' : options[:new_case]

  # don't attempt to change if string is ONLY whitespace
  unless string.match?(/\A\s*\z/)
    # only word characters will be changed,
    string.chars.each_with_index.map { |c, idx| c.match?(/\w/) ? idx : nil }.reject(&:nil?)
        .then { |indicies| indicies.sample(rand(1..indicies.size)) }
        .inject(string) { |memo, idx| memo[idx] = memo[idx].send(new_case); memo }
  end
  string
end

Я пошел дальше, чтобы игнорировать строку, если это только пробел. Он также будет игнорировать любые символы в строке, которые не являются символами слова. Я также добавил опцию по умолчанию к виду I w sh для использования. Это позволит, upcase, downcase и swapcase.

0 голосов
/ 30 января 2020

Вот три способа, которые могут обеспечить псевдослучайность .

Все используют строку

str = "WoLf"  

для демонстрации и метод

def change_possible?(str, to_case)
  str.count(to_case == :downcase ? 'a-z' : 'A-Z') < str.size       
end

, чтобы определить, может ли процедура рандомизации произвести строку, отличную от данной строки, str.

change_possible?("WoLf", :downcase) #=> true
change_possible?("WoLf", :upcase)   #=> true

change_possible?('wolf', :downcase) #=> false
change_possible?('WOLF', :upcase)   #=> false

В последних двух подходах используется метод

def char_case(c)
  c.downcase == c ? :downcase : :upcase
end

char_case('a') #=> :downcase
char_case('A') #=> :upcase

Измените регистр одного случайно выбранного символа, затем случайным образом определите, следует ли установить регистр каждого из оставшихся символов в указанный регистр 1

def scramble(str, to_case)
  return nil unless change_possible?(str, to_case)
  i = str.size.times.reject do |i|
    char_case(str[i]) == to_case
  end.sample
  c = str[i].public_send(to_case)
  str.gsub(/./) { |s| [s, s.public_send(to_case)].sample }.
      tap { |s| s[i] = c }
end

str                         #=> "WoLf" 
scramble(str, :downcase)    #=> "Wolf" 
scramble(str, :downcase)    #=> "woLf" 
scramble(str, :downcase)    #=> "wolf" 
scramble("wolf", :downcase) #=> nil 

scramble(str, :upcase)      #=> "WOLF" 
scramble(str, :upcase)      #=> "WOLf"
scramble(str, :upcase)      #=> "WOLF" 
scramble("WOLF", :upcase)   #=> nil 

Произвольно определить, следует ли устанавливать регистр каждого символа в указанный регистр, повторяя до тех пор, пока результирующая строка не будет отличаться от данной строки

def scramble(str, to_case)
  return nil unless change_possible?(str, to_case)
  loop do
    s = str.gsub(/./) { |c| [c, c.public_send(to_case)].sample }
    break s unless s == str
  end
end

str                         #=> "WoLf" 
scramble(str, :downcase)    #=> "Wolf" 
scramble(str, :downcase)    #=> "woLf"  
scramble(str, :downcase)    #=> "wolf" 
scramble("wolf", :downcase) #=> nil 

scramble(str, :upcase)      #=> "WOLF" 
scramble(str, :upcase)      #=> "WoLF" 
scramble(str, :upcase)      #=> "WoLF" 
scramble("WOLF", :upcase)   #=> nil 

Обратите внимание, что c.public_send(to_case) может равняться c в [c, c.public_send(to_case)].sample.

Создайте пробное пространство, затем выберите случайного члена

def scramble(str, to_case)
  return nil unless change_possible?(str, to_case)
  sample_space(str, to_case).sample
end

def sample_space(str, to_case)
  fixed_idx = str.size.times.select do |i|
    char_case(str[i]) == to_case
  end
  len = str.size
  (0..2**str.size - 1).map do |n|
    len.times.with_object('') do |i,s|
      s << (n[i] == 1 ? str[i].upcase : str[i].downcase)
    end
  end.reject do |s|
    s == str || fixed_idx.any? { |i| s[i] != str[i] }
  end
end

str                         #=> "WoLf" 
scramble(str, :downcase)    #=> "wolf" 
scramble(str, :downcase)    #=> "woLf" 
scramble(str, :downcase)    #=> "Wolf"
scramble("wolf", :downcase) #=> nil 

scramble(str, :upcase)      #=> "WOLF" 
scramble(str, :upcase)      #=> "WOLF" 
scramble(str, :upcase)      #=> "WoLF"
scramble("WOLF", :upcase)   #=> nil 

Для :downcase пространство выборки было найдено

["wolf", "Wolf", "woLf"]

Для :upcase,

["WOLf", "WoLF", "WOLF"]

Пространство образца изначально содержит 2**str.size #=> 16 элементов, причем между каждым элементом выборочного пространства и 1 из целых чисел от 0 до 2**str.size - 1 имеется карта 1-1. Отображение соответствует значению целочисленных битов (с добавлением начальных нулей, если необходимо, до str.size битов), 0 соответствует нижнему регистру, 1 верхнему регистру. Недопустимые строки затем удаляются из пробного пространства.

См. Integer # [] и обратите внимание, что n[i] может равняться 0, 1 или nil, с nil соответствует ведущему 0.

Я не претендую на эффективность этого подхода.

1. Это вариант подхода Стефана.

0 голосов
/ 30 января 2020

Я бы сделал что-то вроде этого:

def random_case_changes(string: string, case: 'downcase')
  if string.is_a?(String)
    string.chars.map { |char| [char, char.public_send(change_case)].sample }.join
  else
    raise ArgumentError, "Param is #{string.class}, not a string"
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...