Modo efficiente di valutare un array di stringhe quindi aggiungere a un array in Ruby

1

Sto cercando un modo efficace per valutare una serie di stringhe rispetto a una serie di regole e quindi aggiungerle a un altro array.

Ad esempio, ho una serie di stringhe:

  1. qualcosa
  2. a
  3. 1234
  4. #gohere

Voglio ripetere tutte le voci nell'array e valutarle in base a una serie di regole. Ad esempio:

  • Stringhe maggiori di 1 lunghezza
  • Stringhe che non iniziano con il # carattere

Stavo pensando a:

  1. Ciclo annidato di istruzioni if che valutano la stringa per ogni regola e se passano tutte quindi aggiungono a un altro array
  2. Valutazione della stringa rispetto alla regola 1, quindi aggiunta all'array, quindi esecuzione di un'altra valutazione: l'ho scontato poiché richiede molte operazioni di lettura / scrittura dell'array per valutare il set di regole completo.

Si noti che ho mantenuto il numero di regole a due per brevità, tuttavia potrei aver bisogno di un massimo di 10 regole. Inoltre, non controllo l'array originale poiché la funzione che sto chiamando restituisce un array di stringhe.

So che potrei aver risposto alla mia domanda con il ciclo annidato, tuttavia mi chiedevo se qualcuno avesse qualche idea per ottimizzare questo tipo di situazione.

Inoltre, so di aver posto questa domanda nel riferimento di Ruby, ma ogni pseudo-codice sarebbe sufficiente.

    
posta Matt S 17.08.2013 - 12:27
fonte

4 risposte

1

Userei la matrice # selezionare e passare un blocco che & & s insieme tutti i test. Se i test diventano ingombranti, puoi astrarli in un metodo o in una serie di metodi.

    
risposta data 17.08.2013 - 15:23
fonte
1

Rendi ogni regola una lambda, come questa che restituisce true se la lunghezza della stringa è maggiore di 1:

->(s) {s.length > 1}

Crea una matrice di tutte le regole che una stringa deve superare affinché corrisponda. A meno che la lista delle regole non cambi, rendila una costante:

RULES = [
  ->(s) {s.length > 1},
  ->(s) {s =~ /^#/},
]

Se una delle regole è complicata, dai loro la propria costante, il nome della costante che serve come commento. Come per la risposta di @ sebastiangeiger, ordina le regole in base alla probabilità di fallimento. Vuoi fallire il prima possibile, se la velocità è essenziale.

Per vedere se una stringa corrisponde, usa "any?" predicato:

RULES.all? do |rule|
  rule.call(s)
end

# Enumerable tutti? short-curcuits, quindi la prima regola che restituisce false impedirà la valutazione delle restanti regole per quella stringa.

Ora, per trovare tutte le stringhe che corrispondono a tutte le regole:

matching_strings = strings.select do |string|
  RULES.all? do |rule|
    rule.call(string)
  end
end
    
risposta data 24.01.2014 - 09:21
fonte
0

Sospetto che un for loop o un select siano uguali in termini di efficienza. Puoi sempre implementare entrambe le opzioni e benchmark .

Ciò che puoi ottimizzare è il corpo dell'iterazione. Se hai più regole e ti unisci a quelle insieme usando and ruby, farai valutazione di cortocircuito e restituirai dopo ha trovato il primo valore false . Quindi l'ordine di tali regole è importante.

Ora devi scoprire:

  1. Quale regola è più probabile che restituisca false
  2. Quale regola valuterà più rapidamente

Ordina le regole dal più veloce al più lento e / o più probabile che non riescano ad avere meno probabilità di fallire. Senza un problema specifico, tuttavia, non posso darti una risposta più specifica. E come per tutto ciò che riguarda l'ottimizzazione, misura sempre per primo.

Esempio totalmente inventato

require 'benchmark'
def slow_and_false
  sleep 0.1
  false
end

def fast_and_false
  sleep 0.005
  false
end

Benchmark.bm do |x|
  x.report("slow first") do
    100.times do
      slow_and_false and fast_and_false
    end
  end
  x.report("fast first") do
    100.times do
      fast_and_false and slow_and_false
    end
  end
end

Il risultato di questo:

            user     system      total        real
slow first  0.000000   0.000000   0.000000 ( 10.086344)
fast first  0.010000   0.010000   0.020000 (  0.560307)
    
risposta data 17.08.2013 - 17:04
fonte
0

Ho fornito la risposta di seguito a una domanda simile a Stack Overflow :

module Checks
  def _invalid_permission() nil end
  def _check_another_error() "oops!" end
  def _and_another_one() nil end
end

class Doit
  @@checks = Checks.instance_methods(false)
  include Checks

  def doit
    @@checks.each { |m| rv = send(m); return rv if rv }

    "test"
  end
end

p Doit.new.doit # => "oops!"

Se cambi

def _check_another_error() "oops!" end

a

def _check_another_error() nil end

poi

p Doit.new.doit # => "test"

Ovviamente, i metodi di controllo possono essere chiamati con argomenti.

Questo approccio ti consente di aggiungere, rimuovere o rinominare un metodo di controllo senza dover ricordare di cambiare i riferimenti ad esso altrove.

    
risposta data 24.01.2014 - 07:20
fonte

Leggi altre domande sui tag