Using n-best lists
Last Updated: 05-22-2009

When developing voice applications, scenarios may arise that require you to clarify the user's response:

Consider a stock quote application. When asked for a company name the user may say a name that sounds similar to multiple company names. For example, if the user says, "Cisco," a voice recognition system will not be able to differentiate between "Cisco Systems" and "Sysco Foods" without additional context information.

The following example defines a SRGS grammar that includes both "Sysco Foods" and "Cisco Systems." Because the words "foods" and "systems" are optional, the user can say "Sysco" or "Cisco."


  <rule id="root_rule" scope="public">
    <one-of>
      <item>
        <one-of>
          <item>
            sysco
            <item repeat="0-1">
              foods
            </item>
          </item>
        </one-of>
        <tag>out.ticker = "SYSC"; out.coname = "Sysco Foods";</tag>
      </item>
      <item>
        <one-of>
          <item>
            cisco
            <item repeat="0-1">
              systems
            </item>
          </item>
        </one-of>
        <tag>out.ticker = "CSCO"; out.coname = "Cisco Systems";</tag>
      </item>
      <!-- additional equities -->
    </one-of>
  </rule>


To handle this situation, an advanced stockquote application can detect the ambiguity by examining a list of likely possibilities returned by the voice recognizer. This "n-best" list allows the voice application to expand the dialog between the system and the user as follows:

Tellme: Say a company name.
Caller: Cisco
Tellme: Press one for Cisco Systems; press two for Sysco Foods.
Caller: (user types 1)
Tellme: Cisco systems, up 1 to 19.

Consider also a company directory application in which the user may say the name of an employee with a common name such as "John Smith." A partial SRGS grammar follows:


  <rule id="root_rule" scope="public">
    <one-of>
      <item>
        <one-of>
          <item>
            john smith
          </item>
        </one-of>
        <tag>out.empid = "1234"; out.ext = "9010";</tag>
      </item>
      <item>
        <one-of>
          <item>
            sally donovan
          </item>
        </one-of>
        <tag>out.empid = "1212"; out.ext = "9015";</tag>
      </item>
      <item>
        <one-of>
          <item>
            john smith
          </item>
        </one-of>
        <tag>out.empid = "8355";  out.ext = "9020";</tag>
      </item>
      <!-- additional employees -->
    </one-of>
  </rule>


In this situation, an advanced directory application can detect the ambiguity and process the n-best list to expand the dialog between the system and the user as follows:

Tellme: Say the first and last name of an employee.
Caller: John Smith
Tellme: There are two employees named John Smith. Press one for John Smith at extension 9010; press two for John Smith at extension 9020.
Caller: (user types 2)
Tellme: Hold on while I transfer you to John Smith at extension 9020.

Rather than providing the single most likely result, the voice recognizer can provide a list of multiple, likely possibilities. The list of likely possibilities is known as an n-best list, and this article explains how to take advantage of the Tellme VoiceXML interpreter's support for n-best lists.

1. Enabling n-best processing

Normally, the recognizer returns a single result from the active grammar. To instruct the recognizer to return multiple results, set the maxnbest property to an integer greater than one, the default value. In the following code snippet, the recognizer will return at most 4 results.

<property name="maxnbest" value="4"/>

Note. You should only enable n-best processing in a dialog that references a grammar that is necessarily ambiguous or in a dialog that is likely to cause the user to provide input that yields a recognition result with low confidence. The VoiceXML interpreter and underlying subsystems performs additional computation to provide the additional results. Every time the value of maxnbest is doubled, recognition performance decreases by 4%. For example, setting maxnbest to 128 results in roughly 22% slower recognition compared with setting the property to 4.

2. Obtaining the results

With the maxnbest property set to a value greater than or equal to one, the VoiceXML interpreter stores an array of objects in the application-scoped lastresult$ variable. The array is accessible from within the filled element after a successful recognition. The array is indexed starting from 0. To determine the number of results returned by the interpreter, check the length property of the array.

var iResultCount = application.lastresult$.length

Each object in the array represents a result from the voice recognizer and contains several properties to help you decide whether or not to verify or override the most likely result picked by the recognizer.

Note. The array is cleared and repopulated upon each recognition, so be sure to examine the contents of the array before entering the next listen state.

confidence A decimal value from 0.0 to 1.0 indicating the recognizer's confidence that the utterance matches what the user actually said. The objects in the array are sorted by the value of this property in descending order.
inputmode The mode of user input - "voice" or "dtmf".
interpretation The return value from the grammar. Only use this if each entry in the grammar returns a single value. If a grammar entry returns multiple values simultaneously, the value of this property is non-deterministic. Use the tellmeinterpretations property instead.
tellmeinterpretations An object, the properties of which represent the interpretation assigned to a particular grammar slot. The name of each property is equivalent to the name of the slot, and its value is the interpretation in string form. This property is a Tellme extension.
tellmeslotconfidences An object, the properties of which represent the voice recognizer's confidence value for a particular slot. The name of each property is equivalent to the name of the slot, and its value is the confidence between 0.0 and 1.0. Due to recognizer limitations, you must fill slots using a subgrammar in order for the properties of this object to hold meaningful values. This property is a Tellme extension.
utterance The word or phrase spoken by the user or the DTMF sequence if the touchtone keypad was used.

The following code snippet simply iterates through the n-best list and writes the utterance, confidence value, and grammar return values of each element in the list to the debug log.


<filled>
  <script>
      var aBest = application.lastresult$;
      var iResults = aBest.length; // how many results did we get back

      // walk the results
      for (var i = 0; i &lt; iResults; i++)
      {
         var oCurBest = aBest[i];
         var sUtt = oCurBest.utterance; // what the user said
         var sRetVals = Retvals2String(oCurBest); // return value(s) and confidence(s)
         var nConf = oCurBest.confidence; // how likely overall
         vxmllog("utterance=" + sUtt + ", overall confidence=" + nConf + 
               ", return value(s)=" + sRetVals);
      }

      // return values and likelihoods as a formatted string
      function Retvals2String(oBest)
      {
        var sResults = "";

        // code defensively since this property is experimental 
        // and may be deprecated in the future
        if ('object' == typeof(oBest.tellmeinterpretations))
        {
            // regardless of whether the grammar returns a single value
            // or multiple values, the tellme* properties are filled
            var oInterps = oBest.tellmeinterpretations;
            var oConfs = oBest.tellmeslotconfidences;
            for (var key in oInterps)
            {
               sResults += (key + "=" + oInterps[key] + 
                        " (" + (oConfs[key] || "?") + "), ");
            }
            sResults = sResults.replace(/, $/, "");
        }
        else
        {
            // grammar may still fill multiple slots,
            // but this is simpler in the single return value case
            sResults += (oBest.interpretation + " (" + oBest.confidence + ")");
        }
        
      	return sResults;
      }
  </script>
</filled>


3. Implementing a skip list

When the recognizer selects the best match from the active grammars, your application code typically uses the value stored in the form item variable to determine the next call state. In noisy environments or when the active grammars contain confusable entries, the recognizer may return a value with a low confidence score. Under those circumstances your application can confirm with the user the choice made by the recognizer. If the user invalidates the recognized result, the application reprompts the user for the same information. Upon this and further iterations, however, the application should exclude the choice that was invalidated by the user. To keep track of the excluded choices, you implement a "skip list."

3.1. Determining recognition confidence

To determine the recognizer's confidence in a recognition result, you can check the confidence shadow variable of the field element as shown in the city field in the following example:

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<var name="nThreshold" expr="0.60"/>

<form name="get_city">
  <field name="city">
    <prompt>Say a city</prompt>
    <grammar src="cities-voice.grxml" mode="voice" type="application/srgs+xml"/>
    <filled>
      <if cond="city$.confidence &gt; nThreshold">
        <!-- use the result; skip confirmation -->
        <assign name="confirm" value="true"/>
      <else/>
        <!-- allow confirm field to be selected -->
      </if>
    </filled>
  </field>

  <field name="confirm" type="boolean">
    <prompt>
    I heard you say <value expr="city"/>.
    Is that correct?
    </prompt>
    <filled>
      <if cond="confirm">
        <!-- use the result -->
      <else/>
        <assign name="city" expr="undefined"/>
        <assign name="confirm" expr="undefined"/>
      </if>
    </filled>
  </field>

  <field name="use_city">
    <block>
      Okay, <value expr="city"/>
      <exit/>
    </block>
  </field>  
</form>
</vxml>

In the previous code example, the variable nThreshold is used to establish a confidence threshold below which the application will allow the Form Interpretation Algorithm (FIA) to select the confirm field to verify the result with the user. If the user validates the result, the application proceeds to the use_city field. Otherwise, the application returns to the city field. Since this application doesn't keep track of the utterances rejected by the user, there's a good chance that the user will be asked to re-confirm a value that she has previously rejected. To prevent that, the code must maintain a skiplist.

The following example uses a "skip list" and a confirmation dialog to help the user specify a city. The implementation of the "skip list" is encapsulated in a user-defined ECMAScript class, SkipList, that exposes the following methods:

Init(aInterps) Initializes the skip list. The skip list is initialized when an instance of the class is created, but you can reuse an instance by explicitly calling the Init. To pre-load the skip list you can pass an ECMAScript array of interpretations.
AddItem(sInterp) Adds an interpretation to the skip list. The interpretation is the right-hand-side of the grammar. You typically call this method in your confirmation dialog if the user invalidates the result you are confirming.
FindFirstAbsent(aResults) You pass in application.lastresult$, and this method finds the first element in the array that is not present in the skip list.
ShouldSkip(oResult) You pass in a single element from application.lastresult$, and this method returns true if the corresponding interpretation is in the skip list or false otherwise. The ShouldSkip method always returns false when passed a result based on DTMF input because confidence in DTMF input is always 100%.
IgnoreLinks(bIgnore) By default, the SkipList class ignores recognition results that correspond to link elements. If you wish to handle links in your application code in conjunction with this class, pass the parameter true to the IgnoreLinks method. All subsequent calls to FindFirstAbsent and ShouldSkip will consider link elements.

Here's the code that uses the SkipList class:

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<script src="skipmgr.js"/>
<var name="oSkipList" expr="new SkipList()"/>
<var name="nConfidenceThreshold" expr="0.60"/>

<var name="docsCity"/>

<form id="get_city">
  <field name="city">
    <property name="maxnbest" expr="4"/>
    <prompt>Say a city</prompt>
    <grammar src="cities-voice.grxml" mode="voice" type="application/srgs+xml"/>
    <grammar src="cities-dtmf.grxml" mode="dtmf" type="application/srgs+xml"/>
    <nomatch>
      Sorry. I didn't get that.
      <reprompt/>
    </nomatch>
    <noinput>
      Sorry. I didn't hear you.
      <reprompt/>
    </noinput>
    <filled>
      <var name="oCandidate" expr="oSkipList.FindFirstAbsent(lastresult$)"/>
      <if cond="null == oCandidate">
        <!-- all of the results were in the skip list! -->
        <assign name="city" expr="undefined"/>
        <throw event="nomatch"/>
      <else/>
        <assign name="docsCity" expr="oCandidate.interpretation"/>
        <if cond="oCandidate.confidence &gt; nConfidenceThreshold">
          <!-- use this one -->
          <goto next="#use_city"/>  
        <else/>
          <!-- low confidence; let's make sure this is what they meant -->
          <goto next="#confirm_city"/>
        </if>
      </if>
    </filled>
  </field>
</form>

<form id="confirm_city">
  <field name="ok" type="boolean">
    <prompt>I heard you say <value expr="docsCity"/>.
      Is that correct?
    </prompt>
    <catch event="noinput nomatch">
      Sorry. I didn't get that.
      Please say yes or no.
    </catch>
    <filled>
      <if cond="ok">
        <goto next="#use_city"/>
      <else/>
        <script>oSkipList.AddItem(docsCity)</script>
        <assign name="docsCity" expr="undefined"/>
        <goto next="#get_city"/>
      </if>
    </filled>
  </field>
</form>

<form id="use_city">
  <!-- use the city specified by the user -->
  <block>
    You said <value expr="docsCity"/>
    <exit/>
  </block>
</form>

</vxml>

The application sources in the SkipList class contained in the file "skipmgr.js". The variable oSkipList references an instance of the SkipList class. The variable nConfidenceThreshold stores the confidence threshold upon which utterances are confirmed with the user. The variable docsCity allows the recognition result to be shared between the get_city, confirm_city, and use_city dialogs.

In the get_city dialog, the maxnbest property is set to 4 to allow up to four utterances to be returned by the recognizer in decreasing order of confidence. In the filled, the code first calls SkipList.FindFirstAbsent, passing the recognition results referenced by application.lastresult$ to obtain the most likely result that is not in the skip list. If none of the results qualify, the code throws a nomatch event which causes the user to be reprompted for a city.

Note. The code sets the city form item variable to undefined rather than using the clear element to preserve the current values of the event counters (e.g. nomatch, noinput) maintained by the interpreter as part of the FIA.

If SkipList.FindFirstAbsent returned a result, the code then checks the confidence score of the result. If the confidence was low, the code navigates to a confirmation dialog (confirm_city); otherwise, it uses the result (use_city). The confirmation dialog, confirm_city, verifies the utterance selected by SkipList.FindFirstAbsent. If the user invalidates the result, the code adds the utterance to the skip list by calling SkipList.AddItem.

Here's the implementation of the SkipList class:

// A simple skiplist management class
// c-tor
function SkipList(aSkip) 
{ 
  this.Init(aSkip); 
}

// initialize the skip list
// you can pre-load the skiplist by passing in an array (aSkip) of interpretations, 
// equivalent to the right-hand-side of a grammar
SkipList.prototype.Init = function(aSkip)
{
  this._oSkipList = {};
  this._iSkipItems = 0;
  this._bIgnoreLinks = true; // ignore links by default

  if ('object' == typeof(aSkip))
  {
    for(var i = 0; i < aSkip.length; i++)
    {
      this.AddItem(aSkip[i]);
    }
  }
  return true;
}

// Allow client code to deal with links in reco result
SkipList.prototype.IgnoreLinks = function(bIgnore)
{
  var bOldIgnore = this._bIgnoreLinks;
  if ('boolean' == typeof(bIgnore))
  {
    this._bIgnoreLinks = bIgnore;
  }
  return bOldIgnore;
}

// Should this candidate be skipped?
// yes if this is a link and we're ignoring them
// yes if this isn't dtmf input and it's in the skip list
SkipList.prototype.ShouldSkip = function(oCandidate)
{
  var bSkip = false;
  if (this._bIgnoreLinks && /^https?:\/\//.test(oCandidate.interpretation) ||
    (oCandidate.inputmode != "dtmf" && 
     this._oSkipList[oCandidate.interpretation] != null))
  {
    bSkip = true;
  }
  return bSkip;
}

// Return the first item in aCandidates that isn't in the skip list
SkipList.prototype.FindFirstAbsent = function(aCandidates)
{
  // If all possible matches are in the skip list, return null.
  var oRet = null;

  try
  {
    // if skiplist is empty, return the first value in the possibleMatches;
    // recognizer had the most confidence in that one
    if (0 == this._iSkipItems)
    {
      oRet = aCandidates[0];
    }
    else
    {
      // find first entry in aCandidates absent from skiplist.
      for (var i = 0; i < aCandidates.length; i++)
      {
        var oCandidate = aCandidates[i];
        if (!this.ShouldSkip(oCandidate))
        {
          oRet = oCandidate;
          break;
        }        
      }
    }
  }
  catch (e)
  {
    if ('object' == typeof(aCandidates) && aCandidates.length > 0)
    {
      oRet = aCandidates[0];
    }
  }

  return oRet;
}

// Add an item to the skiplist
SkipList.prototype.AddItem = function(sInterp)
{
  if (null == this._oSkipList[sInterp])
  {
    this._oSkipList[sInterp] = 1;
    this._iSkipItems++;
  }

  return this._iSkipItems;
}


4. Using lastresult$ to access slot data

Regardless of whether or not you take advantage of n-best processing, if the entries in your grammar return multiple slots per utterance, you can use the lastresult$ array to access the data in each slot via the tellmeinterpretations object property. For example, the following grammar excerpt accepts the name of a state and returns the state code, the capital city and the full name of the state in slots named "code", "capital", and "state" respectively. The slot names map directly to properties of the tellmeinterpretations property.

<?xml version= "1.0"?>


<grammar mode="voice"
         root="root_rule"
         tag-format="semantics/1.0"
         version="1.0"
         xml:lang="en-US"
         xmlns="http://www.w3.org/2001/06/grammar">
  <rule id="root_rule" scope="public">
    <one-of>
      <item>
        alabama
        <tag>out.code = "al"; out.capital = "montgomery"; out.state = "alabama";</tag>
      </item>
      <item>
        alaska
        <tag>out.code = "ak"; out.capital = "juneau"; out.state = "alaska";</tag>
      </item>
      <item>
        arizona
        <tag>out.code = "az"; out.capital = "phoenix"; out.state = "arizona";</tag>
      </item>
      <!--__strip_content=on -->
      <item>
        arkansas
        <tag>out.code = "ar"; out.capital = "little rock"; out.state = "arkansas";</tag>
      </item>
      <item>
        california
        <tag>out.code = "ca"; out.capital = "sacramento"; out.state = "california";</tag>
      </item>
      <item>
        colorado
        <tag>out.code = "co"; out.capital = "denver"; out.state = "colorado";</tag>
      </item>
      <item>
        connecticut
        <tag>out.code = "ct"; out.capital = "hartford"; out.state = "connecticut";</tag>
      </item>
      <item>
        deleware
        <tag>out.code = "de"; out.capital = "dover"; out.state = "deleware";</tag>
      </item>
      <item>
        florida
        <tag>out.code = "fl"; out.capital = "tallahassee"; out.state = "florida";</tag>
      </item>
      <item>
        georgia
        <tag>out.code = "ga"; out.capital = "atlanta"; out.state = "georgia";</tag>
      </item>
      <item>
        hawaii
        <tag>out.code = "hi"; out.capital = "honolulu"; out.state = "hawaii";</tag>
      </item>
      <item>
        idaho
        <tag>out.code = "id"; out.capital = "boise"; out.state = "idaho";</tag>
      </item>
      <item>
        illinois
        <tag>out.code = "il"; out.capital = "springfield"; out.state = "illinois";</tag>
      </item>
      <item>
        indiana
        <tag>out.code = "in"; out.capital = "indianapolis"; out.state = "indiana";</tag>
      </item>
      <item>
        iowa
        <tag>out.code = "ia"; out.capital = "des moines"; out.state = "iowa";</tag>
      </item>
      <item>
        kansas
        <tag>out.code = "ks"; out.capital = "topeka"; out.state = "kansas";</tag>
      </item>
      <item>
        kentucky
        <tag>out.code = "ky"; out.capital = "frankfort"; out.state = "kentucky";</tag>
      </item>
      <item>
        louisiana
        <tag>out.code = "la"; out.capital = "baton rouge"; out.state = "louisiana";</tag>
      </item>
      <item>
        maine
        <tag>out.code = "me"; out.capital = "augusta"; out.state = "maine";</tag>
      </item>
      <item>
        maryland
        <tag>out.code = "md"; out.capital = "annapolis"; out.state = "maryland";</tag>
      </item>
      <item>
        massachusetts
        <tag>out.code = "ma"; out.capital = "boston"; out.state = "massachusetts";</tag>
      </item>
      <item>
        michigan
        <tag>out.code = "mi"; out.capital = "lansing"; out.state = "michigan";</tag>
      </item>
      <item>
        minnesota
        <tag>out.code = "mn"; out.capital = "saint paul"; out.state = "minnesota";</tag>
      </item>
      <item>
        mississippi
        <tag>out.code = "ms"; out.capital = "jackson"; out.state = "mississippi";</tag>
      </item>
      <item>
        missouri
        <tag>out.code = "mo"; out.capital = "jefferson city"; out.state = "missouri";</tag>
      </item>
      <item>
        montana
        <tag>out.code = "mt"; out.capital = "helena"; out.state = "montana";</tag>
      </item>
      <item>
        nebraska
        <tag>out.code = "ne"; out.capital = "lincoln"; out.state = "nebraska";</tag>
      </item>
      <item>
        nevada
        <tag>out.code = "nv"; out.capital = "carson city"; out.state = "nevada";</tag>
      </item>
      <item>
        new
        hampshire
        <tag>out.code = "nh"; out.capital = "concord"; out.state = "new hampshire";</tag>
      </item>
      <item>
        new
        jersey
        <tag>out.code = "nj"; out.capital = "trenton"; out.state = "new jersey";</tag>
      </item>
      <item>
        new
        mexico
        <tag>out.code = "nm"; out.capital = "santa fe"; out.state = "new mexico";</tag>
      </item>
      <item>
        new
        york
        <tag>out.code = "ny"; out.capital = "albany"; out.state = "new york";</tag>
      </item>
      <item>
        north
        carolina
        <tag>out.code = "nc"; out.capital = "raleigh"; out.state = "north carolina";</tag>
      </item>
      <item>
        north
        dakota
        <tag>out.code = "nd"; out.capital = "bismarck"; out.state = "north dakota";</tag>
      </item>
      <item>
        ohio
        <tag>out.code = "oh"; out.capital = "columbus"; out.state = "ohio";</tag>
      </item>
      <item>
        oklahoma
        <tag>out.code = "ok"; out.capital = "oklahoma city"; out.state = "oklahoma";</tag>
      </item>
      <item>
        oregon
        <tag>out.code = "or"; out.capital = "salem"; out.state = "oregon";</tag>
      </item>
      <item>
        pennsylvania
        <tag>out.code = "pa"; out.capital = "harrisburg"; out.state = "pennsylvania";</tag>
      </item>
      <item>
        rhode
        island
        <tag>out.code = "ri"; out.capital = "providence"; out.state = "rhode island";</tag>
      </item>
      <item>
        south
        carolina
        <tag>out.code = "sc"; out.capital = "columbia"; out.state = "south carolina";</tag>
      </item>
      <item>
        south
        dakota
        <tag>out.code = "sd"; out.capital = "pierre"; out.state = "south dakota";</tag>
      </item>
      <item>
        tennessee
        <tag>out.code = "tn"; out.capital = "nashville"; out.state = "tennessee";</tag>
      </item>
      <item>
        texas
        <tag>out.code = "tx"; out.capital = "austin"; out.state = "texas";</tag>
      </item>
      <item>
        utah
        <tag>out.code = "ut"; out.capital = "salt lake city"; out.state = "utah";</tag>
      </item>
      <item>
        vermont
        <tag>out.code = "vt"; out.capital = "montpelier"; out.state = "vermont";</tag>
      </item>
      <item>
        virginia
        <tag>out.code = "va"; out.capital = "richmond"; out.state = "virginia";</tag>
      </item>
      <!--__strip_content=off -->
      <item>
        washington
        <tag>out.code = "wa"; out.capital = "olympia"; out.state = "washington";</tag>
      </item>
      <item>
        west
        virginia
        <tag>out.code = "wv"; out.capital = "charleston"; out.state = "west virginia";</tag>
      </item>
      <item>
        wisconsin
        <tag>out.code = "wi"; out.capital = "madison"; out.state = "wisconsin";</tag>
      </item>
      <item>
        wyoming
        <tag>out.code = "wy"; out.capital = "cheyenne"; out.state = "wyoming";</tag>
      </item>
    </one-of>
  </rule>

</grammar>

The filled element in the following stores a reference to the first element of the lastresult$ array in the anonymously scoped variable named oTopResult. The capital and full name of the state are accessed via the tellmeinterpretations property exposed by oTopResult and are read back to the user.

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<link event="capitals.onquit">
   
   <grammar mode="voice"
         root="root_rule"
         tag-format="semantics/1.0"
         type="application/srgs+xml"
         version="1.0"
         xml:lang="en-US">
      <rule id="root_rule" scope="public">
         <one-of>
            <item>
               quit
            </item>
         </one-of>
      </rule>

   </grammar>

</link>

<catch event="capitals.onquit">
   <exit/>
</catch>

<form id="pick_state">

<field name="state">

<prompt>
Say the name of a state, 
and I'll tell you the capital city.
</prompt>

<grammar src="states-voice.grxml" mode="voice" type="application/srgs+xml"/>

<catch event="noinput nomatch" count="1">
   Sorry. I didn't get that. Say the name of a U S state.
</catch>

<catch event="noinput nomatch" count="2">
   Sorry. I didn't get that. Say a state.
   Or say quit to exit at any time.
</catch>

<catch event="noinput nomatch" count="3">
   <exit/>
</catch>

<filled>
   <script><![CDATA[
      var oTopResult = application.lastresult$[0];       
      var sCapital = oTopResult.tellmeinterpretations.capital;
      var sState = oTopResult.tellmeinterpretations.state;
   ]]></script>
   The capital of <value expr="sState"/> is <value expr="sCapital"/>
   <clear namelist="state"/>
</filled>
</field>
</form>
</vxml>

[24]7 Inc.| Terms of Service| Privacy Policy| General Disclaimers