git clone https://github.com/cmirnow/atm-on-grape.git
Вы легко освоите описанную механику, посетив и клонировав репозиторий atm-on-grape, также следуя нехитрым указаниям документации. Сейчас же кратко остановлюсь всего на одном.
Понятно, что основой логики и наиболее интересным моментом здесь является автоматический расчет требуемой суммы, исходя из числа имеющихся купюр определенных номиналов. Разберем ряд способов реализации этого механизма, часть которых вполне возможно отнести к области динамического программирования.
Два простейших решения выглядят следующим образом:
def subset_sum(inp, s)
arr = []
loop.with_index{|_, i| arr << inp.combination(i+1).to_a.find{
|c| (c.inject(0) {|sum,x| sum + x}) == s}; break if !a.none? || i == inp.count }
arr
end
subset_sum([1,2,3,4,5,6], 15)
def subset_sum(*args)
args[0].length.downto(1).flat_map do |i|
args[0].combination(i).to_a
end.select do |a|
a.inject(:+) == args[1]
end
end
subset_sum([1,2,3,4,5,6], 15).first
Это отлично работает, пока первый аргумент вызова метода содержит массив из нескольких элементов (купюр), но вы не представляете, как безбожно способен затянуться процесс расчета, если количество "купюр" некритично больше; скажем, два или три десятка. В этом случае оптимально прибегнуть к такой рекурсии:
def subset_sum((first, *rest), target)
return (first == target ? [first] : nil) if rest.empty?
rv = subset_sum(rest, target)
return rv unless rv.nil?
return nil if first > target
return [first] if first == target
rv = subset_sum(rest, target - first)
rv.nil? ? nil : [first, *rv]
end
print subset_sum([1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 1, 2], 15)
Или даже так:
def subset_sum(inp, sum)
candidates = inp.filter { |i| i < sum } # don't do this optimize if inp contain negative numbers
sum_combi_hash = Hash.new do |hash, key|
if key > 0
hash[key] = { candidates[key] => [candidates[key]] }
if hash[key - 1][sum].nil?
hash[key - 1].each do |s, c|
hash[key][s] = c
new_s = s + candidates[key]
next unless hash[key][new_s].nil?
hash[key][new_s] = c + [candidates[key]]
break if new_s == sum
end
else
hash[key][sum] = hash[key - 1][sum]
end
hash[key]
else
hash[key] = { candidates.first => [candidates.first] }
end
end
sum_combi_hash[candidates.size - 1][sum]
end
print subset_sum([1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 1, 2], 15)
Вы сумеете провести успешные тесты описанной логики (также всего API), воспользовавшись простейшим минитестом:
require 'test_helper'
class Banknote::AssetTest < ActiveSupport::TestCase
include Rack::Test::Methods
def app
Rails.application
end
test 'GET /api/v1/banknote_asset' do
get('/api/v1/banknote_asset')
assert last_response.ok?
assert_equal 200, last_response.status
assert_equal 58, last_response.length
end
test 'Successful cash withdrawal' do
patch('/api/v1/banknote_asset?sum=93')
assert_equal '[1, 2, 5, 10, 25, 50]', last_response.body
end
test 'Unable to issue required amount' do
patch('/api/v1/banknote_asset?sum=94')
assert_equal 'transaction not allowed', last_response.body
end
end
Таким образом - ценность, на субъективный взгляд автора, представляют не деньги, виртуальные либо же реальные, а - сам по себе процесс познания. Воспользуйтесь моим эмпирическим опытом, если кого-то он заинтересует.
Комментарии в блоге