Мартин обнови решението на 31.12.2014 15:46 (преди над 3 години)
+package main
+
+import (
+ "errors"
+ "regexp"
+ "strconv"
+ "sync"
+)
+
+const (
+ DOES_NOT_EXIST = iota
+ ENQUEUED
+ IN_PROGRESS
+ UNABLE_TO_PRODUCE
+ DONE
+)
+
+type Order struct {
+ Id string
+ Status int //Състояние, което се обновява от фабриката
+ Words []string // Думи, за които трябва да бъде произведен
+ // регулярен израз
+ Result string // Произведеният регулярен израз
+ Channel chan *Order // Канал, по който да бъде върната
+ // поръчката, когато бъде приключена
+}
+
+var nextId int
+
+//Създава нова поръчка с подадените думи и канал. Тази функция трябва да генерира уникално Id.
+func NewOrder(words []string, channel chan *Order) *Order {
+ result := new(Order)
+ result.Id = strconv.Itoa(nextId)
+ nextId++
+ result.Status = ENQUEUED
+ result.Words = make([]string, 0)
+ result.Result = ""
+ result.Channel = channel
+ return result
+
+}
+
+type Factory struct {
+ materials map[string]uint16
+ OrderChan chan *Order
+ Orders []*Order
+ stop chan interface{}
+ pushEvent chan interface{}
+ mutex sync.Mutex
+}
+
+func runWorker(factory *Factory) {
+ for {
+ select {
+ case o, _ := <-factory.OrderChan:
+ o.Result, _ = factory.generateRegexp(o.Words)
+ o.Status = DONE
+ case _, _ = <-factory.stop:
+ break
+ }
+ }
+}
+
+//Създава фабрика с подадения брой работници и започва да изпълнява поръчки.
+func NewFactory(workers uint8) *Factory {
+ result := new(Factory)
+ result.materials = make(map[string]uint16)
+ result.Orders = make([]*Order, 0)
+ result.pushEvent = make(chan interface{})
+ result.stop = make(chan interface{})
+ for i := uint8(0); i < workers; i++ {
+ go runWorker(result)
+ }
+ return result
+}
+
+//Добавя поръчката в списъка на чакащите да бъдат изпълнени.
+func (f *Factory) Enqueue(order *Order) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+ f.Orders = append(f.Orders, order)
+ go func(pushEvent chan interface{}) {
+ _, _ = <-pushEvent
+ }(f.pushEvent)
+}
+
+//Започва да изпълнява поръчки.
+func (f *Factory) StartProducing() {
+ for {
+ for _, val := range f.Orders {
+ val.Status = IN_PROGRESS
+ f.OrderChan <- val
+ }
+ f.pushEvent <- struct{}{}
+ }
+
+}
+
+//Прекратява производството като изпълнява само поръчките със статус IN_PROGRESS.
+func (f *Factory) StopProducing() {
+ close(f.stop)
+}
+
+//Приема map с материал: количество и добавя съответните материали и техните количества в склада.
+func (f *Factory) StorageAdd(materials map[string]uint16) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+ for key, val := range materials {
+ f.materials[key] += val
+ }
+}
+
+//приема стрингове и връша парче което мачва навалото на всички стрингове ако няма такова връща ""
+func (f *Factory) recursiveGenerateRegexp(words []string, materials map[string]uint16) []string {
+ for idx, _ := range materials {
+ nextStepWords := make([]string, 0)
+ for _, word := range words {
+ r, err := regexp.Compile("^" + idx)
+ if err != nil {
+ continue
+ }
+ if r.MatchString(word) {
+ nextStepWords = append(nextStepWords, r.ReplaceAllString(word, ""))
+ }
+ }
+ if len(nextStepWords) != len(words) {
+ continue
+ }
+ nextStep := false
+ for _, newWord := range nextStepWords {
+ if newWord != "" {
+ nextStep = true
+ break
+ }
+ }
+ if nextStep {
+ nMaterials := make(map[string]uint16)
+ nMaterials = materials
+ nMaterials[idx] -= 1
+ tokens := f.recursiveGenerateRegexp(nextStepWords, nMaterials)
+ if len(tokens) != 0 {
+ result := make([]string, 0)
+ result = append(result, idx)
+ result = append(result, tokens...)
+ return result
+ }
+ } else {
+ result := make([]string, 0)
+ return append(result, idx)
+ }
+ }
+ return make([]string, 0)
+}
+
+//Генерира регулярен израз (или връща грешка при недостатъчно материали), който match-ва всички думи в слайса words. Върнатият регулярен израз трябва да започва с ^ и да завършва с $.
+func (f *Factory) generateRegexp(words []string) (string, error) {
+
+ mat := make(map[string]uint16)
+ mat = f.materials
+ result := f.recursiveGenerateRegexp(words, mat)
+ pay := func(tokens []string, factory *Factory) bool {
+ factory.mutex.Lock()
+ defer factory.mutex.Unlock()
+ for idx, val := range tokens {
+ _, ok := factory.materials[val]
+ if !ok {
+
+ for ; idx > 0; idx-- {
+ factory.materials[tokens[idx]] += 1
+ }
+ return false
+ }
+ factory.materials[val] -= 1
+ }
+
+ return true
+ }
+ stringResult := ""
+ if pay(result, f) && len(result) != 0 {
+ for _, val := range result {
+ stringResult += val
+ }
+ } else {
+ return "", errors.New("Not enough materials")
+ }
+ return "^" + stringResult + "$", nil
+}
+
+type Client struct {
+ Name string
+ Orders map[string]*Order // Всички поръчки от този клиент
+ // където ключът е Order.Id
+ Channel chan *Order // Канал, по който клиентът
+ // получава поръчка в момента,
+ // в който тя бъде приключена
+}
+
+//Създава нов клиент с подаденото име.
+func NewClient(name string) *Client {
+ result := new(Client)
+ result.Name = name
+ result.Orders = make(map[string]*Order)
+ return result
+
+}
+
+//Създава нова поръчка с думи, изпраща я на подадената фабрика и връща id-то на поръчката.
+func (c *Client) Order(factory *Factory, words []string) string {
+
+ result := NewOrder(words, c.Channel)
+ factory.Enqueue(result)
+ c.Orders[result.Id] = result
+ return result.Id
+}
+
+//Проверява статуса на клиентска поръчка с подаденото id.
+func (c *Client) CheckStatus(id string) int {
+ _, ok := c.Orders[id]
+ if ok {
+ return c.Orders[id].Status
+ }
+ return DOES_NOT_EXIST
+}