Sonlu Durumlu Makinelere Örnekler [kapalı]


25

Sonlu durum makinelerinin iyi örneklerini arıyorum; dil özellikle önemli değil, sadece iyi örnekler.

Kod uygulamaları yararlıdır (genelleştirilmiş sözde kod), ancak FSM'lerin çeşitli kullanımlarını toplamak da çok yararlıdır.

Örneklerin mutlaka bilgisayar tabanlı olması gerekmez, örneğin Mike Dunlavey'nin Demiryolu ağları örneği çok faydalıdır.


12
Düzenli ifadeler sonlu durum makineleridir.
chrisaycock

5
Neden bu sorunun 'yapıcı değil' olarak işaretlendiğini anlamıyorum. İlk kapatıldığına dair bir cevap verdiğimden bu yana neredeyse 2 yıl geçtiğini düşünürdüm, aslında çok yapıcı ve konuyla ilgili olduğunu savunuyorum.
aqua

2
@ aqua - aslında stack ile ilgili bir problem daha var. Programcılar, görevi zorunlu, bir cevabı olan çok özel soruları cevaplamak. Ancak, bunun gibi değerli olan sorular, genel olarak Stack sitelerinin tanımlarına dayanarak “yapıcı” (IMNSHO'da terimin çok zayıf bir tanımı) olarak kabul edilmez. Açıkçası, Programcıların gerçekten yararlı olması için, bu özel kurala daha az gayretli bir bağlılık benimsemelidir;
ocodo

1
Etkili olan asıl sorun, Stack sitelerinin açıkçası, iyi bilinen ve işbirlikçi olan ve iyi ve okunabilir bir biçime sahip çok az sayıda yüksek kaliteli kaynaktan biri olmasıdır. (. Muhtemelen E kelimesini kullanmadan) O Yığın bu indirgemecilik, eğitici "sorularına" içindir sitesi biçimi için bir ihtiyaca gerçekten noktaları görünür
ocodo

3
İnsanlara bu soruyu yeniden açmaları için hala yalvarıyorum, çünkü daha fazla örnek harika olurdu. Üzücü gerçek, eğer yeni cevaplar eklenmemiş olsaydı, soru açık kalırdı.
ocodo

Yanıtlar:


28

Güvenli (olay tetiklendi)

  • Durumlar : Birden fazla "kilitli" durum, bir "açık" durum
  • Geçişler : Doğru kombinasyonlar / tuşlar, sizleri kilitleninceye kadar ilk kilitli durumlardan, kilitlenmeye yakın olan kilitli durumlara götürür. Yanlış kombinasyonlar / anahtarlar sizi ilk kilitli duruma getirir (bazen boşta olarak da bilinir) .

Trafik Işığı (zaman tetiklenir | sensör [olay] tetiklenir)

  • Birleşik Devletler : KIRMIZI, SARI, YEŞİL (en basit örnek)
  • Geçişler : Bir zamanlayıcıyı KIRMIZI, YEŞİL, SARI, SARI KIRMIZI olarak değiştirdikten sonra. Otomobilleri çeşitli (daha karmaşık) durumlarda algılamak da tetiklenebilir.

Otomatik Satış Makinesi (olay tetiklendi, kasanın bir varyasyonu )

  • Durumlar : IDLE, 5_CENTS, 10_CENTS, 15_CENTS, 20_CENTS, 25_CENTS, vb, VEND, DEĞİŞTİR
  • Geçişler : Paralar, faturalar, doğru satın alım miktarında (veya daha fazla) VEND'e geçiş, ardından DEĞİŞİM veya IDLE'ye geçiş (Vending Machine'inizin ne kadar etik olduğuna bağlı olarak) durumu değiştirir

Yapabilseydim +1 ve daha fazlası. Sonuncusu dışında her şey iyi görünüyor. IDLE, VEND ve CHANGE olmalıdır. Değerler şartlıdır ve IDLE ile kendisi arasındaki geçiş olarak gösterilmelidir. Bir öğenin seçildiğini temsil eden bir devlet de istersiniz.
Evan Plaice,

@EvanPlaice: Bir öğeyi seçmek, IDLE'den VEND'e bir değişikliği tetikleyen olay olmaz mıydı? Satış yapmadan önce seçimi onaylama yöntemini öngörmüyorsanız.
Misko,

Bu ikisi için bir diyagram şansı var mı?
ocodo

Bunlar aynı zamanda asansörleri de içeren FSM Wikipedia sayfasında verilen örneklerle aynıdır : "Basit örnekler , uygun madeni paralar biriktirildiğinde ürünleri dağıtan satış otomatları , durma sırasını istenen katlara göre belirleyen asansörlerdir. biniciler, arabalar beklerken sırayı değiştiren trafik ışıkları ve kombinasyon numaralarının doğru sırada girilmesini gerektiren kombinasyon kilitleri . "
icc97

1
@ icc97 FSM örnekleri günlük yaşam boyunca bol ve yaygındır. Bu arada, borsanın değişim
aqua

14

Sınır Geçidi Protokolü Örneği

BGP, İnternet'teki çekirdek yönlendirme kararlarını destekleyen bir protokoldür. Belirli bir düğümden ana bilgisayarların erişilebilirliğini belirlemek için bir tablo tutar ve interneti gerçekten merkezi olmayan hale getirir.

Ağda, her bir BGP düğümü eşdüzeydir ve altı durumdan biri Boşta , Bağlan , Aktif , OpenSent , OpenConfirm ve Kurulan sonlu durumlu bir makine kullanır . Ağdaki her eş bağlantı bu durumlardan birini korur.

BGP protokolü, durumlarını değiştirmek için eşlere gönderilen mesajları belirler.

BPG istatistik çizelgesi.

BGP istatistik çizelgesi

boş

İlk devlet boşta . Bu durumda, BGP kaynakları başlatır ve gelen bağlantı girişimlerini reddeder ve eş ile bir bağlantı başlatır.

Bağlan

İkinci durum Bağlan . Bu durumda, yönlendirici bağlantının tamamlanmasını bekler ve başarılı olursa OpenSent durumuna geçer. Başarısız olursa, ConnectRetry zamanlayıcısını sıfırlar ve sona erme tarihinde Aktif durumuna geçer.

Aktif

Gelen Aktif duruma, yönlendirici sıfırdan ve getirilere ConnectRetry zamanlayıcı sıfırlar Bağlan devlet.

OpenSent

In OpenSent devlet, yönlendirici bir Açık mesajı ve karşılığında biri için bekler gönderir. Keepalive mesajları değiş tokuş edilir ve başarılı bir şekilde alındığında, yönlendirici Kuruluş durumuna getirilir .

kurulmuş

In kurulan devlet, yönlendirici / göndermek alabilir: Canlı tutma; Güncelleştirme; ve eşine gelen / gelen bildirim mesajları.

BGP hakkında daha fazla bilgi


@tcrosley - bu wikipedia'dan geliyor, bu yüzden gerçekten kredi hak etmiyor.
ocodo

1
tamam +1 içeren bir diyagramı. :)
tcrosley

Grafiğin etiketini
BGP'ye

Bunu çizmenin daha iyi bir yolu olmalı.
Meslek

1
@Job - biraz gecikmiş bir cevap, üzgünüm, ama şimdi bunun çok ezoterik bir örnek olduğunu düşünüyorum. Güvenli, Otomatik Satış Makinesi vb. Bence çok daha faydalı.
ocodo

7

Her türlü şeyi modellemek için faydalıdırlar. Örneğin, bir seçim döngüsü (normal hükümet) - seçim denilen -> (erken kampanya) - Parlamento dağılmış -> (ağır kampanya) - seçim -> (oy sayma) çizgileri boyunca devletlerle modellenebilir ). Sonra ya (oy sayma) - çoğunluk yok -> (koalisyon müzakereleri) - anlaşmaya varıldı -> (normal hükümet) veya (oy sayma) - majörlük -> (normal hükümet). Siyasi alt oyunu olan bir oyunda bu şema üzerinde bir çeşitleme uyguladım.

Oyunların başka yönlerinde de kullanılıyorlar: AI genellikle eyalet merkezlidir; Menüler ve seviyeler arasındaki geçişler ve ölüm veya seviye tamamlandıktan sonraki geçişler genellikle FSM'ler tarafından iyi modellenmiştir.


++ Güzel örnek.
Mike Dunlavey

1
+1 Yollar nedeniyle iyi bir DFM örneği (Deterministik Sonlu Durum Makinesi).
Evan Plaice,

4

Jquery-csv eklentisinde kullanılan CSV ayrıştırıcısı

Temel bir Chomsky Tip III gramer ayrıştırıcısı.

Regex tokenizer, verileri karakter bazında değerlendirmek için kullanılır. Bir kontrol karakteriyle karşılaşıldığında, başlangıç ​​durumuna bağlı olarak daha fazla değerlendirme yapmak için kod bir switch deyimine geçirilir. Kontrol dışı karakterler, gerekli dize kopyalama işlemlerinin sayısını azaltmak için toplu olarak gruplandırılır ve kopyalanır.

Belirteç:

var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;

İlk eşleşme kümesi kontrol karakterleridir: değer sınırlayıcı (") değer ayırıcı (,) ve giriş ayırıcı (yeni satırın tüm varyasyonları) Son eşleşme kontrol dışı karakter grubunu işler.

Ayrıştırıcının yerine getirmesi gereken 10 kural vardır:

  • Kural # 1 - Her satıra bir giriş, her satır yeni bir satırla bitiyor
  • Kural # 2 - İhmal edilen dosyanın sonunda son satır
  • Kural # 3 - İlk satır başlık verisi içeriyor
  • Kural # 4 - Boşluklar veri olarak kabul edilir ve girdiler sonunda virgül içermemelidir
  • Kural 5 - Satırlar çift tırnakla sınırlandırılabilir veya sınırlandırılmayabilir
  • Kural # 6 - Satır sonları, çift tırnak işaretleri ve virgüller içeren alanlar çift tırnak işaretleri içine alınmalıdır
  • Kural # 7 - Alanları içine almak için çift tırnak işareti kullanılıyorsa, o zaman bir alan içinde görünen çift tırnak işareti, başka bir çift tırnak işareti ile başlayarak önüne geçilmelidir.
  • Değişiklik # 1 - Belirtilmeyen bir alan olabilir veya olabilir
  • Değişiklik # 2 - Alınan bir alan olabilir veya olmayabilir
  • Değişiklik # 3 - Bir girdideki son alan boş değer içerebilir veya içermeyebilir

Not: İlk 7 kural doğrudan IETF RFC 4180'den alınmıştır . Son 3, varsayılan olarak tüm değerleri sınırlamayan (yani alıntı yapan) modern elektronik tablo uygulamaları (örneğin, Excel, Google Elektronik Tablo) tarafından sunulan son durumları kapsayacak şekilde eklendi. Değişiklikleri RFC'ye geri göndermeye çalıştım, ancak soruşturmamı henüz yanıtlamadılar.

Kuruma kadar yeter, işte şema:

CSV ayrıştırıcı sonlu durum makinesi

Devletler:

  1. bir giriş ve / veya değer için başlangıç ​​durumu
  2. bir açılış teklifi ile karşılaşıldı
  3. ikinci bir teklifle karşılaşıldı
  4. alıntılanmamış bir değerle karşılaşıldı

Geçişler:

  • a. alıntılanan değerleri (1), alıntılanmamış değerleri (3), boş değerleri (0), boş girişleri (0) ve yeni girişleri (0) denetler
  • b. ikinci bir alıntı char için kontrol eder (2)
  • c. Çıkış teklifini (1), değerin (0) ve girişin (0) sonu kontrol eder
  • d. değerin (0) sonunu ve girişin (0) sonunu denetler

Not: Aslında bir devlet eksik. 'C' -> 'b' ile '1' durumuyla işaretlenmiş bir satır olmalıdır, çünkü kaçan ikinci bir sınırlayıcı, ilk sınırlayıcının hala açık olduğu anlamına gelir. Aslında, onu başka bir geçiş olarak göstermek muhtemelen daha iyi olacaktır. Bunları yaratmak bir sanattır, tek bir doğru yol yoktur.

Not: Ayrıca bir çıkış durumu da eksiktir, ancak geçerli verilerde ayrıştırıcı her zaman 'a' geçişiyle sona erer ve ayrıştırılacak hiçbir şey olmadığı için hiçbir durum mümkün değildir.

Durumlar ve Geçişler Arasındaki Fark:

Bir devlet sonludur, yani yalnızca bir şeyi ifade ettiği sonucuna varılabilir.

Bir geçiş, devletler arasındaki akışı temsil eder, böylece birçok anlama gelebilir.

Temel olarak devlet -> geçiş ilişkisi 1 -> * (yani bir-çok). Devlet “ne olduğunu” ve geçiş “nasıl işlendiğini” tanımlar.

Not: Durumların / geçişlerin uygulaması sezgisel hissetmiyorsa endişelenmeyin, sezgisel değildir. Sonunda yapışmadan önce, benden çok daha zeki biriyle mukabil olmuştu.

Sahte Kod:

csv = // csv input string

// init all state & data
state = 0
value = ""
entry = []
output = []

endOfValue() {
  entry.push(value)
  value = ""
}

endOfEntry() {
  endOfValue()
  output.push(entry)
  entry = []
}

tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm

// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
  switch(state) {
    case 0:
      if(opening delimiter)
        state = 1
        break
      if(new-line)
        endOfEntry()
        state = 0
        break
      if(un-delimited data)
        value += match
        state = 3
        break
    case 1:
      if(second delimiter encountered)
        state = 2
        break
      if(non-control char data)
        value += match
        state = 1
        break
    case 2:
      if(escaped delimiter)
        state = 1
        break
      if(separator)
        endOfValue()
        state = 0
        break
      if(newline)
        endOfEntry()
        state = 0
        break
    case 3:
      if(separator)
        endOfValue()
        state = 0
        break
      if(newline)
        endOfEntry()
        state = 0
        break
  }
}

Not: Bu özü, pratikte dikkate alınması gereken daha çok şey var. Örneğin, hata kontrolü, boş değerler, sonunda boş bir satır (geçerli olan) vb.

Bu durumda, durum, regex eşleştirme bloğu bir yinelemeyi tamamladığında durumların durumudur. Geçiş, durum ifadeleri olarak temsil edilir.

İnsanlar olarak, düşük seviyeli işlemleri daha yüksek seviyeli özetlere basitleştirme eğilimindeyiz, ancak bir FSM ile çalışmak düşük seviyeli işlemlerle çalışıyor. Durumlar ve geçişler bireysel olarak çalışmak çok kolay olsa da, hepsini bir kerede görselleştirmek zordur. Geçişlerin nasıl yürüdüğünü sezene kadar, bireysel uygulama yollarını tekrar tekrar izlemeyi en kolay buldum. Temel matematik öğrenmek gibi bir şeydir, düşük seviye detayları otomatikleşmeye başlayana kadar kodu daha yüksek bir seviyeden değerlendirme olanağınız olmayacaktır.

Bir kenara: Asıl uygulamaya bakarsanız, birçok ayrıntı eksik. İlk olarak, tüm imkansız yollar özel istisnalar atar. Onlara vurmak imkansız olmalı, ancak bir şey koparsa test koşucusunda istisnaları kesinlikle tetikleyecektir. İkincisi, 'yasal' bir CSV veri dizisinde izin verilenler için ayrıştırıcı kurallar oldukça gevşek olduğundan kod birçok özel durumla başa çıkmak için gerekli. Bu durumdan bağımsız olarak, bu, tüm hata düzeltmelerinden, uzantılardan ve ince ayarlardan önce FSM ile alay etmek için kullanılan işlemdi.

Çoğu tasarımda olduğu gibi, uygulamanın kesin bir temsili değildir, ancak önemli kısımları ana hatlarıyla belirtir. Uygulamada, aslında bu tasarımdan türetilmiş 3 farklı ayrıştırıcı işlev vardır: csv'ye özgü bir satır ayırıcı, tek satırlı bir ayrıştırıcı ve tam bir çok satırlı ayrıştırıcı. Hepsi benzer şekilde çalışır, yeni satır karakterlerini kullanma şekilleri farklıdır.


1
çüş! çok hoş bir katkı.
ocodo

@Slomojo Teşekkürler, paylaştığım için mutluyum. CS için okula gitmedim, bu yüzden bunları kendi başıma öğrenmek zorunda kaldım. Bu çevrimiçi gibi üst düzey CS konularını kapsayan gerçek dünyadaki uygulamaları bulmak gerçekten zor. Sonunda çözümleyici algoritmasını ayrıntılı bir şekilde belgelendirmeyi planlıyorum, böylece gelecekte benim gibi başkaları için yararlı olabilir. Bu iyi bir başlangıç.
Evan Plaice,

Ben de kendi kendime öğrendim, ancak 30 yıl önce başladım, bu yüzden şimdi CS müfredatını ele aldım :) Bu soruyu senin ve benim gibi insanlar için yayınladım. O zamanlar çok düşük seviyeli bir teori öğrenmenin çok daha kolay olduğunu düşünüyorum, çünkü daha az dikkat dağıtıcı ve metale yakın çalışmak için daha fazla fırsat vardı, çünkü gerçekten bir internet yoktu ve hepimiz mağaralarda yaşıyorduk.
ocodo,

3

Java'da basit FSM

int i=0;

while (i<5) {
 switch(i) {
   case 0:
     System.out.println("State 0");
     i=1;
     break;
   case 1:
     System.out.println("State 1");
     i=6;
     break;
   default:
     System.out.println("Error - should not get here");
     break;      
  }

} 

İşte gidiyorsun. Tamam, parlak değil, ama fikri gösteriyor.

FSM'leri telekom ürünlerinde sıklıkla bulacaksınız çünkü karmaşık bir durum için basit bir çözüm sunarlar.


3
Bunlar aynı zamanda sözcüksel analizde derleyici yapımının önemli bir parçasıdır.
jmq

@jmquigley, lütfen bir cevap ekler misiniz?
ocodo

1
Sizin için birkaç link içeren ayrı bir cevap ekledim.
jmq

3

Asansör (asansör) hakkında düşünmenin / modellemenin sonlu durumlu bir makineye iyi bir örnek olduğunu buldum. Giriş yolunda çok az şey gerektirir, ancak uygulanması önemsiz bir durumdan çok uzaktır.

Devletler örneğin zemin katta, birinci katta vb. Ve zemin birinci kata hareket ediyor ya da üçüncü kata hareket ediyor ancak şu anda 3. ve 2. katlar arasında hareket ediyor.

Düğmelerin kaldırma kafesi içindeki ve katlardaki kendileri, etkisi mevcut durumla birlikte basılan her iki düğmeye de bağlı olan girdiler sağlar.

Üst ve alt hariç her katta iki düğme bulunur: biri asansörün yukarı çıkmasını istemek için diğeri aşağı inmek için.


2

Tamam, işte bir örnek. Bir tamsayı ayrıştırmak istediğinizi varsayalım. Bir tamsayı hanesi dd*olduğu gibi dbir şey giderdi .

state0:
    if (!isdigit(*p)) goto error;
    p++;
    goto state1;
state1:
    if (!isdigit(*p)) goto success;
    p++;
    goto state1;

Elbette, @Gary’in dediği gibi, bunları gotobir switch ifadesi ve durum değişkeni ile gizleyebilirsiniz . Orjinal normal ifadeye eşdeğer olan bu koda yapılandırılabilene dikkat edin:

if (isdigit(*p)){
    p++;
    while(isdigit(*p)){
        p++;
    }
    // success
}
else {
    // error
}

Tabii ki arama tablosuyla da yapabilirsiniz.

Sonlu durum makineleri birçok yolla yapılabilir ve birçok şey sonlu durum makinelerinin örnekleri olarak tanımlanabilir. Bir şeyler hakkında düşünmek için bir kavram kadar bir şey değil.

Demiryolu Ağı Örneği

Bir FSM örneği, demiryolu ağıdır.

Bir trenin iki yoldan birine girebileceği sınırlı sayıda anahtar vardır.

Bu anahtarları birbirine bağlayan sınırlı sayıda parça vardır.

Herhangi bir zamanda, bir tren bir yol üzerindedir, tek bir bit girdi bilgisine dayanarak bir şalterden geçerek başka bir parçaya gönderilebilir.


(Cevabınızı değiştirdim, umarım onaylarsınız.)
ocodo

@Slomojo: Bu iyi. İyi görünüyor.
Mike Dunlavey

2

Ruby'de Sonlu Durum Makinesi:

module Dec_Acts
 def do_next
    @now = @next
    case @now
    when :invite
      choose_round_partner
      @next = :wait
    when :listen
      @next = :respond
    when :respond
      evaluate_invites
      @next = :update_in
    when :wait
      @next = :update_out
    when :update_in, :update_out
      update_edges
      clear_invites
      @next = :exchange
    when :exchange
      update_colors
      clear_invites
      @next = :choose
    when :choose
      reset_variables
      choose_role
    when :done
      @next = :done
    end
  end
end

Bu, dağıtılmış bir sistemdeki tek bir hesaplama düğümünün davranışı, bağlantıya dayalı bir iletişim düzeni oluşturuyor. Az çok. Grafik biçiminde şöyle bir şey görünüyor:

görüntü tanımını buraya girin


+1 İlginç. DGMM ne anlama gelir?
Evan Plaice

@EvanPlaice dağıtılmış Maksimum Eşleşmeye Dayalı Minimum Ağırlıklı Vertex Kapak Algoritması (DGMM) ... biraz kısaltılmış bir kısaltma, G'nin nereden geldiğini sorma.
ocodo

@slomojo "G", "Genelleştirilmiş" anlamına gelir; bunun türetildiği sıralı algoritma, genelleştirilmiş maksimum eşleştirme adı verilen bir teknik kullanır.
philosodad

@ philosodad Çok fazla varsaydım ama varsayımlarda bulunmaktan hoşlanmıyorum.
ocodo


0

Uygulamada, Devlet Makineleri sık sık şunlar için kullanılır:

  • Tasarım amaçları (bir programdaki farklı eylemlerin modellenmesi)
  • Doğal dil (gramer) ayrıştırıcıları
  • Dize ayrıştırma

Bir örnek, doğru sözdizimine sahip olup olmadığını görmek için bir dize tarayan bir Durum Makinesi olabilir. Örneğin Hollanda Posta Kodları "1234 AB" olarak biçimlendirilmiştir. İlk bölüm yalnızca rakamları, yalnızca ikinci harfleri içerebilir. NUMBER durumunda mı yoksa LETTER durumunda mı olduğunu ve yanlış girdiyle karşılaşıp karşılaşmadığını reddeden bir Durum Makinesi yazılabilir.

Bu alıcı durum makinesinin iki durumu vardır: sayısal ve alfa. Durum makinesi sayısal durumda başlar ve kontrol edilecek dizenin karakterlerini okumaya başlar. Durumlardan herhangi biri sırasında geçersiz karakterlerle karşılaşılırsa, işlev Yanlış bir değerle döner ve girişi geçersiz olarak reddeder.

Python kodu:

import string

STATE_NUMERIC = 1
STATE_ALPHA = 2

CHAR_SPACE = " "

def validate_zipcode(s):
cur_state = STATE_NUMERIC

for char in s:
    if cur_state == STATE_NUMERIC:
        if char == CHAR_SPACE:
            cur_state = STATE_ALPHA
        elif char not in string.digits:
            return False
    elif cur_state == STATE_ALPHA:
        if char not in string.letters:
            return False
return True

zipcodes = [
    "3900 AB",
    "45D6 9A",
]

for zipcode in zipcodes:
    print zipcode, validate_zipcode(zipcode)

Kaynak: (Sonlu -) Devlet Makineleri Uygulamada

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.