2013-10-15 10 views
13

Sono nel processo di apprendimento Ruby, prendendo MOOC del Berkeley, e, in alcuni dei compiti di questi MOOC abbiamo un esercizio che dice:Come creare un ciclo annidato con Ruby "Right Way!"?

definire un metodo sum_to_n? che accetta un array di interi e un intero aggiuntivo , n, come argomenti e restituisce true se due elementi nella matrice di numeri interi sono pari a n. Un array vuoto deve sommare a zero per definizione.

Ho già creato due metodi che possono svolgere il lavoro, ma non mi sento a mio agio con nessuno di loro perché penso che non siano scritti in Ruby Way. Spero che alcuni di voi possano aiutarmi ad imparare quale sarebbe la strada giusta!

Il primo metodo che ho fatto utilizza il metodo each per entrambe le iterazioni, ma quello che non mi piace di questo metodo è che ogni numero viene sommato ad ogni altro numero, anche con lo stesso numero, fare qualcosa di simile:

arr[1, 2, 3, 4] => 1+1, 1+2, 1+3, 1+4, 2+1, 2+2, 2+3, 2+4, 3+1, 3+2... 4+3, 4+4 

Come potete vedere, ci sono un sacco di somme ripetute, e io non voglio quello.

Questo è il codice:

def sum_to_n?(arr, n) 
    arr.each {|x| arr.each {|y| return true if x + y == n && x != y}} 
    return true if n == 0 && arr.length == 0 
    return false 
end 

Con l'altro metodo ho ottenuto quello che volevo, a pochi somme senza ripetere uno di essi o anche sommando gli stessi numeri, ma sembra orribile, e io' m abbastanza sicuro che qualcuno piacerebbe uccidermi per fare in questo modo, ma il metodo fa un ottimo lavoro come si può vedere:

arr[1, 2, 3, 4] => 1+2, 1+3, 1+4, 2+3, 2+4, 3+4 

questo è il codice:

def sum_to_n?(arr, n) 
    for i in 0..arr.length - 1 
    k = i + 1 
    for k in k..arr.length - 1 
     sum = arr[i] + arr[k] 
     if sum == n 
     return true 
     end 
    end 
    end 
    return true if n == 0 && arr.length == 0 
    return false 
end 

Bene, spero che voi ragazzi vi divertiate a fare un metodo migliore e più grazioso come ho provato.

Grazie per il vostro aiuto.

+0

Normalmente, vorrei essere stanco di usare 'for' mai in Ruby, ma in questo caso, è accettabile in quanto il secondo metodo avrà meno iterazioni rispetto all'utilizzo di 'each'. –

+2

Mentre Ruby ha 'for', tendiamo a ignorarlo perché ci sono alcuni effetti collaterali, come lasciare la sua variabile intermedia sospesa per ingombrare lo spazio variabile, e ci costringe a scorrere il contenitore usando gli indici calcolati, piuttosto che permettere a 'each' di passare in ogni oggetto individualmente. Gli indici che mancano del primo o dell'ultimo elemento, o che cadono alla fine, sono errori comuni in tutte le lingue e "ciascuno" aiuta ad evitarlo. Quindi, mentre * potrebbe * sembrare una scelta stilistica, è davvero una scelta di programmazione difensiva. E benvenuti a Stack Overflow! –

+0

Appartently, "Massive Open Online Courses" di Berkeley stanno generando un enorme interesse per SO =) –

risposta

18

che avrei scritto così:

def sum_to_n?(arr, n) 
    return true if arr.empty? && n.zero? 
    arr.combination(2).any? {|a, b| a + b == n } 
end 

che sembra essere una soluzione abbastanza Rubyish.

+1

Dannazione. Sono un Rubyist e sono persino sorpreso di come Rubyish sia stata la soluzione. Molto bene. –

+0

Questo è esattamente ciò che intendevo. –

+4

Sei un ragazzo intelligente, non ho dubbi a riguardo. Puoi illuminarci con una risposta generica a questa domanda? Il tuo lavoro perché esiste un metodo che fa ciò che l'OP vuole esiste, ma cosa succede se hai un ciclo annidato generico? O non dovresti averne, e se lo fai lo stai facendo male? – fotanus

1

Questo farà in O(n.log(n)) piuttosto che O(n²): risposta

a = 1, 2, 3, 4 

class Array 
    def sum_to? n 
    unless empty? 
     false.tap { 
     i, j, sorted = 0, size - 1, sort 
     loop do 
      break if i == j 
      a, b = sorted[i], sorted[j] 
      sum = a + b 
      return a, b if sum == n 
      sum < n ? i += 1 : j -= 1 
     end 
     } 
    end 
    end 
end 

a.sum_to? 7 #=> [3, 4] 
+1

Non è O (n) se usa l'ordinamento di 'matrice'. – Amadan

+0

Hai ragione, incluso 'matrice # sort', è (probabilmente)' O (n.log (n)) ', ha modificato la risposta. –

2

Accanto @ di Jorg-w-Mittag. Ho trovato un'altra soluzione usando "permutazione".

https://stackoverflow.com/a/19351660/66493

def sum_to_n?(arr, n) 
    (arr.empty? && n.zero?) || arr.permutation(2).any? { |a, b| a + b == n } 
end 

non sapeva di permutazione prima. Ancora come @ risposta jorg-w-mittag perché è più leggibile.

+0

'permutazione' considera l'ordine importante. Ciò si tradurrà in somme ridondanti, ad es. 'a + b' e' b + a'. Pertanto "combinazione" è la scelta migliore in questo caso. –

4

Mi sono imbattuto in questo CodeWars. Il accepted answer sembra davvero molto rubino, ma è a costo di prestazioni.Chiamando arr.combination(2) si ottengono molte combinazioni, sarebbe più semplice esaminare l'elemento array per elemento e cercare se il 'complemento' sum - element esiste. Ecco come che sarebbe simile -

def sum_to_n?(arr, n) 
    (arr.empty? and n.zero?) or arr.any? { |x| arr.include?(n - x) } 
end 
+0

Perché hai bisogno della prima espressione parentetica? Gli array vuoti non causeranno "any?" O "include?" Per generare errori. È una buona risposta, penso che potrebbe essere un po 'più conciso. –

+0

@ mark-thomas - "Un array vuoto dovrebbe sommare a zero per definizione" è quello che dice la domanda - Se non fosse per la prima espressione, 'sum_to_n? ([], 0) restituire' false'. – rohitpaulk

0

ho avuto un pensiero che all'inizio di ogni risposta a questa domanda dovrebbe probabilmente iniziare con potatura l'array per i dati superflui:

non possono utilizzare questo:

arr.select! { |e| e <= n } # may be negative values  

ma questo potrebbe aiutare:

arr.sort! 
    while arr[0] + arr[-1] > n # while smallest and largest value > n 
    arr.delete_at(-1) # delete largest vaue 
    end 
Problemi correlati