Rekursion och strängar

Innehåll

  1. Strängar
    • Tokning av strängar
      • Rekursion
        • Uppgifter
          • Redovisning
            • Tyck till

              Uppgiften syftar till att ge kunskap och förståelse för hur:

              1. strängar representeras mha ASCII.
              2. problem kan lösas med rekursion.
              3. stacken används under rekursiva anrop till subrutiner.

              Senast uppdaterat: Tue Nov 29 16:34:56 CET 2005

              ~ karl

              Strängar

              I uppgift ett lärde vi oss hantera ord och arrayer av ord tolkade som heltal. Nu skall vi bekanta oss med en annan typ av arrayer, nämligen arrayer av bytes. För att koda bokstäver representeras varje bokstav av ett specifikt tal. En array av sådana tal kan alltså användas för att koda en sträng av bokstäver.

              Det vanligaste sättet att koda bokstäver är ASCII. Med en sträng menas vanligen en adress till en array av bytes där varje byte tolkas som en bokstav enligt ASCII-kodningen. För att veta när en sträng är slut avslutas den med det speciella tecknet NULL.

              Tokning av strängar

              Meningslöst vetande

              Det längsta kända palindromet på bara ett ord är saippuakivikauppias som är finska och betyder "tvålstensförsäljare".

              För människor är det oftast enkelt att skilja på olika typer av strängar. Genom att tolka innebörden av en sträng kan vi dela in strängar i olika klasser.

              Exempel: strängen "123" kan tolkas som ett possitivt heltal medans strängen "AbBa" inte kan tolkas som ett heltal.

              Ett palindrom (av grekiskans palindromos = tillbakalöpande) är en text som blir likadan om man läser den baklänges. Detta förutsätter att man ignorerar mellanslag, punkter och andra skiljetecken samt inte gör skillnad på liten och stor bokstav.

              Exempel: strängen "123" är inte ett palindrom men strängen "AbBa" är ett palindrom.

              Exempel: strängen "Dromedaren Alpotto planerade mord" är ett palindrom (men inte ett heltal).

              Rekursion

              Vid anrop till subrutiner sparas returadressen i det speciella registret $ra. I bland är det praktiskt att låta en subrutin anropa andra subrutiner och då måste returvärdet i $ra sparas undan innan nästa subrutinanrop. Ett bra ställa att spara undan detta värde är stacken. Ett specialfall av detta är när en subrutin anropar sig själv, sk rekursion.

              Exempel: om vi vill beräkna 5! så vet vi att detta betyder 5! = 1*2*3*4*5 = 120. I pseudokod kan vi definiera definiera en funktion som beräknar n! enligt:

              	 
              	 fakultet(n) {
              	   if (n==0) 
              	       return 1;
              	   else
              	       return n*fakultet(n-1);
              	 }
              	 
                     

              I exemplet ovan ser vi att argumentet till det rekursiva anropet minskar för varje rekursivt steg för att till slut anta värdet n==0 vilket utgör vårt basfall.

              Uppgifter

              Ni skall skriva en subrutin strLength_r som beräknar längden av en null-terminerad sträng mha rekursion. Notera att det finns en given subrutin strLength som beräknar längden på en sträng mha itteration (loop). Tänk ut ett lämpligt basfall och rekursivt steg och skriv sedan en rekursiv version.

              M-x asm-mode

              Ett litet tips för er som använder Emacs. Genom att berätta för vår favorit-editor att vi tänker skriva assemblerkod kommer Emacs att hjälpa oss lite extra. Ettiketter får en egen färg, rader får automatisk indrag mm.

              Ni skall skriva en subrutin strIsInt som kontrollerar om en sträng kan tolkas som ett possitivt heltal eller ej. Ni får själva välja om ni vill skriva en rekursiv lösning eller en iterativ.

              Ni skall skriva en rekursiv subrutin strIsPallindrome_r som kontrollerar om en sträng kan tolkas som ett pallindrom. Ni behöver ej hantera mellanslag eller andra skiljetecken, dock skall det inte spela någon roll om små eller stora bokstäver används.

              Observera att all kod ni skriver skall vara försedd med lämpliga kommentarer. Vad som menas med lämpliga kommentarer kommer att framgå under genomgången. För inspiration och vägledning för hur bra kommentarer kan se ut, titta tex på på kommentarerna i strLength.

              Ni skall bygga vidare på koden nedan:

              [ download ][ maximera ]

              uppgift_tva.s

              ##############################################################################
              ##
              ## DESCRIPTION: Computer Systems 1 - part 1 (DARK)
              ##
              ##              Assignment 2: Strings and recursion.
              ##
              ##              Add a short descriptive summary...
              ##
              ##
              ## AUTHOR(S):   Student One <student.one@student.uu.se>
              ##              Student Two <student.two@student.uu.se>
              ##
              ## HISTORY:     November XX, 2004. First version.
              ##
              ##              November ZZ, 2004. Fixed infinite recurision bug.
              ##
              ##############################################################################
              
              
              
              ##############################################################################
              ## Data Segment
              ##############################################################################
              
              		
                      .data
              
              MSG1:   .asciiz "\nMata in en sträng: "
              MSG2:   .asciiz "\nDu matade in:      "
              
              MSG3:   .asciiz "\nSträngens längd (itteration):   "
              MSG3b:  .asciiz "\nSträngens längd (reursion):   "	
              
              MSG4:   .asciiz "\nSträngen är ett palindrom\n"
              MSG5:   .asciiz "\nSträngen är inte  palindrom\n"
              
              MSG6:   .asciiz "\nSträngen är ett heltal\n"
              MSG7:   .asciiz "\nSträngen är inte ett heltal\n"
              
              NL:     .asciiz "\n"
              
              BUFF:   .space 80       # a 80 character string buffer
              
              
              ##############################################################################
              ## Text Segment
              ##############################################################################
              
              
                      .text
              
                      .globl main
              
              	
              	
              #-----------------------------------------------------------------------------
              # Main Start
              #-----------------------------------------------------------------------------
              
              
              main:   addi    $sp, $sp, -4    # Push return adress
              
                      sw      $ra, 0($sp)
              
                      # "Mata in en stäng:"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG1       # address of string to print
                      syscall                 # print the string
              
              
                      # read string from user into buffert
              
              
                      li      $v0, 8          # system call for read_str
                      la      $a0, BUFF       # address to buffer
                      li      $a1, 80         # length of buffer, will read max 79
                                              # characters and terminate with null
              
                      syscall
              
                      # "du matade in:"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG2       # address of string to print
                      syscall                 # print the string
              
              
                      la      $a0, BUFF       # address of string to print
                      syscall                 # print the string
              
              
              
              
                      #  "Strängens längd är (iteration):"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG3       # address of string to print
                      syscall                 # print the string
              
              
              
                      # Call strLength procedure to get length of string.
              
              
                      la      $a0, BUFF
              
                      jal strLength           # call strLength
              
              
                      move    $s0, $v0        # save the result for future use
              
              
                      # Print the length.
              
              
                      li      $v0, 1          # system call code for print_int
                      move    $a0, $s0        # integer to print ($a0)
                      syscall                 # print the integer
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, NL         # print a new-line
              
                      syscall
              
              
              
                      #  "Strängens längd är (rekursion):"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG3b      # address of string to print
                      syscall                 # print the string
              
              
              
                      # Call strLength procedure to get length of string.
              
              
                      la      $a0, BUFF
              
                      jal strLength_r          # call strLength
              
              
                      move    $s0, $v0        # save the result for future use
              
              
                      # Print the length.
              
              
                      li      $v0, 1          # system call code for print_int
                      move    $a0, $s0        # integer to print ($a0)
                      syscall                 # print the integer
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, NL         # print a new-line
              
                      syscall
              
                      # Call strIsInt to check if string is an integer.
              
              
                      la      $a0, BUFF
                      add     $a1, $s0, $zero
              
                      jal strIsInt
              
                      # Check result and print report.
              
              
                      beq     $v0, $zero, notInteger
              
                      #  "Strängen är ett heltal"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG6       # address of string to print
                      syscall                 # print the string
              
              
                      j testPalindrome
              
              notInteger:
              
                      # "Strängen är inte ett heltal"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG7       # address of string to print
                      syscall                 # print the string
              
              
              testPalindrome:
              
                      # Call strIsPalindrome_r to check if string is a palindrome.
              
              
                      la      $a0, BUFF
                      add     $a1, $s0, $zero
              
                      jal strIsPalindrome_r
              
                      # Check result and print report.
              
              
                      beq     $v0, $zero, notPalindrome
              
                      #  "Strängens är ett palindrom"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG4       # address of string to print
                      syscall                 # print the string
              
              
                      j theEnd
              
              notPalindrome:
              
                      #  "Strängens är inte ett palindrom"
              
              
                      li      $v0, 4          # system call for print_str
                      la      $a0, MSG5       # address of string to print
                      syscall                 # print the string
              
              
              theEnd:
              
              
                      # j main                # uncomment if you want the program to loop forever.
              
              
              
                      lw      $ra, 0($sp)     # pop return adress and return to caller (exit)
              
                      addiu   $sp, $sp, 4
              
                      jr      $ra
              
              #-----------------------------------------------------------------------------
              # Main End
              #-----------------------------------------------------------------------------
              
              
              #------------------------------------------------------------------------------
              # INPUT:        $a0 - Pointer (address to first character)  to  string.
              #
              # OUTPUT:       $v0 - length of string in bytes, terminating null character
              #                     is not counted.
              #------------------------------------------------------------------------------
              
              strLength:
              
                      add     $v0, $zero, $zero       # initialize length to zero
              
              
                      addi    $t1, $zero, 0x20        # ASCII(space)=0x20
              
              
              while:  lb      $t0, 0($a0)             # look at each character (byte) in $t0
                                                      # searh for ending '\n' (ascii 0).
              
              
                      blt     $t0, $t1, done          # Trick! Will stop count as soon
                                                      # a non-printable character is
                                                      # encountered.
              
              
              
                      addi    $a0, $a0, 1             # next character
                      addi    $v0, $v0, 1             # increment counter
              
              
                      j       while
              
              done:   sub     $a0, $a0, $v0           # restore input string pointer
                      jr      $ra                     # return to caller
              
              
              
              #------------------------------------------------------------------------------
              # INPUT:        $a0 - Pointer (address to first character)  to  string.
              #
              # OUTPUT:       $v0 - length of string in bytes, terminating null character
              #                     is not counted.
              #------------------------------------------------------------------------------
              
              strLength_r:
              
                      addi    $sp, $sp, -4    	# Must push return address since we use
                      sw      $ra, 0($sp)     	# a recursive procedure call later.
              
              	
                      add     $v0, $zero, $zero       # initialize result to zero
              
              
                      ##############################
                      #### WRITE YOUR CODE HERE ####
                      ##############################
              
              
              	
                      lw      $ra, 0($sp)     	# pop return address
              
                      addi    $sp, $sp, 4
              	
                      jr      $ra                     # return to caller
              
              
              	
              
              #------------------------------------------------------------------------------
              # INPUT:        $a0 - Pointer to string
              #               $a1 - Length of string (trailing null excluded)
              #
              # OUTPUT:       $v0 - 1 string is integer
              #                   - 0 string is not integer
              #------------------------------------------------------------------------------
              
              strIsInt:
              
                      add     $v0, $zero, $zero       # initialize result to zero
              
              
                      ##############################
                      #### WRITE YOUR CODE HERE ####
                      ##############################
              
              
                      jr      $ra                     # return to caller
              
              
              #------------------------------------------------------------------------------
              # INPUT:        $a0 - Pointer to string
              #               $a1 - Length of string (trailing null excluded)
              #
              # OUTPUT:       $v0 - 1 string is a palindrome
              #                   - 0 string is not a palindrome
              #
              # NOTE:         Although more efficient solutions exists, a recursive
              #               is used as an exercise.
              #------------------------------------------------------------------------------
              
              strIsPalindrome_r:
              
                      addi    $sp, $sp, -4    # Must push return address since we use
                      sw      $ra, 0($sp)     # a recursive procedure call later.
              
              
                      ##############################
                      #### WRITE YOUR CODE HERE ####
                      ##############################
              
              
                      # You shold include the comments below, use them to structure
                      # your solution.
              
              
                      # The empty string is always a palindrome.
              
              
                      # A one-character string is always a palindrome.
              
              
                      # Are the first and last character equal?
              
              
                      # Palindrome check is case-insensitive, e.g., "AbBa"is a palindrome.
                      # If two characters only differs in case, the  absolute value of the
                      # difference bewteen the ASCII-values is exactly 0x20.
              
              
              
                      # If first and last character are equal, if the string in
                      # between is a palindrome the whole string is a palindrome.
                      # Use a recursive procedure call to check this.
              
              
              
                      lw      $ra, 0($sp)     # pop return address
              
                      addi    $sp, $sp, 4
              
                      jr      $ra             # return to caller
              
              

              Redovisning

              Innan ni lämnar in, visst har ni kollat att allt funkar för några "specialfall":
              "" Tom sträng! Är en tom sträng ett palindrom? Är en tom sträng ett heltal?
              "123/4:9" Randvärden, '/' och ':' ligger på gränsen till siffer-tecknen i ASCII-tabellen.
              "aBC99cbA" Kolla att stora och små boksäver inte spelar någon roll.
              "67544576" Ett heltal som är ett palindrom.

              Tyck till

              Som vanligt vill jag veta vad du tycker om denna uppgift. Detta är viktigt för att vi tillsammans skall kunna fortsätta att göra kursen bättre!

              Som helhet tycker jag att uppgiften var:

              Bra
              Hyffsad
              Dålig

              Kommentar:

              Som helhet tycker jag att svårighetsgraden var:

              Lätt
              Lagom
              Svår

              Kommentar:

              Anledningen till att färdiga kodskellet används är att det skall vara lätt att komma igång med en uppgift. Genom att det finns ett färdigt som anropar de olika subrutinerna är det också enkelt att börja testa programmet.

              En invändning mot att använda kodskelett är att det ibland kan vara svårt att förså vad koden gör samt att det vore roligare eller mer lärorikt att skriva allt själv.

              Jag tycker att kodskellet:

              hälper
              stjälper

              Kommentar:

              Vad kan göras bättre:

              Något som du uppskattade speciellt:

              Tidsuppskattning

              Det tog ungefär att göra klart uppgiften.

              Kommentar:

              Övrigt: