Пропуск значения на enumerable#map, если блок кода оценивается как false

Есть ли способ реорганизовать этот метод с помощью карты? (это намерение этого метода состоит в том, чтобы вернуть массив простых чисел между 1 и максимальным параметром)

def primes(max)
  prime_arr = []
   (1..max).each {|i| prime_arr << i if is_prime?(i)}
  prime_arr
end

is_prime?(val) возвращает значение true или false.

если я изменю свой метод на:

def primes(max)
 (1..max).map {|i| i if is_prime?(i)}
end

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

p primes(5)
#=> [1, 2, 3, nil, 5]

Я знаю, что у меня работает, но я бы предпочел не объявлять массив и не возвращать массив, если есть лучший способ сделать это.


person Brent    schedule 31.10.2013    source источник


Ответы (3)


Enumerable.reject (или Enumerable.select) являются правильными методы для фильтрации последовательности.

Эти фильтрующие методы проверяют только истинность возвращаемого значения и, таким образом, при возврате i в этом случае "работают" (поскольку i всегда является истинным значением), возвращаемое значение на самом деле отбрасывается (в отличие от map), поэтому предикат должен выглядеть примерно так:

c.reject {|i| !is_prime?(i)}

# or (inverting the double negative)
c.select {|i| is_prime?(i)}

# or (with &:)
c.select(&:is_prime?)

(См. &: магию последнего случая .)

Кроме того, поскольку здесь речь идет о поиске простых чисел, полезно прочитать Sieve of Eratosthenes. .

person user2864740    schedule 31.10.2013
comment
Да, я понял, что через несколько секунд после публикации вопросов :) отклонить и выбрать - это то, что я искал. - person Brent; 01.11.2013
comment
это был больше вопрос о том, как избежать nils на карте, инжектить и т.д. (я новичок в рубине), я полагаю, что самый правильный способ - это включить Primes и Primes.each через макс. - person Brent; 01.11.2013

require 'Prime'
MAX = 100
p Prime.each(MAX).to_a      #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31...
p (1..MAX).select(&:prime?) #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31...

Альтернативой является использование grep, если вы хотите выбрать и преобразовать:

primes = ->x{x.prime?} # (new lambda syntax)
p (1..100).grep(primes){|x| x.to_s} #=> ["2", "3", "5", "7", "11", "13", "17",...
person hirolau    schedule 31.10.2013
comment
Этот пример grep интересен, но его действительно нужно изучить подробнее (Proc#=== следует упомянуть, потому что это единственная причина, по которой он работает; и лямбда может быть заменена подходом &: к proc). Обычно я просто использую select..map.. в цепочке. - person user2864740; 01.11.2013
comment
Grep использует оператор равенства регистра для выбора элементов. Вы можете использовать имена Regexp или Class. Как вы упомянули, лямбда (или Proc) вызывается по сравнению с ===. Это очень удобно при создании операторов case или при рефакторинге логики сложных цепочек функций. - person hirolau; 01.11.2013
comment
@hirolau - спасибо за информацию! На самом деле я просил выбрать и отклонить (очевидно); я просто не знал, когда я спросил. - person Brent; 01.11.2013

Да... я только что придумал способ, пытаясь объяснить его, но, пожалуйста, не стесняйтесь показать мне лучший способ!

def primes(max)
  (1..max).reject {|i| i if ! is_prime?(i)}
end
person Brent    schedule 31.10.2013