<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="../../../_utils/schema/yaps.rnc" type="application/relax-ng-compact-syntax"?>
<?xml-model href="../../../_utils/schema/yaps.isosch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<?xml-stylesheet type="text/xsl" href="yaps2slidy1.xslt"?>
<!-- $Id: djb_ten-functions.xml 51709 2026-05-28 23:29:13Z syd $ -->
<TEI xmlns="http://www.wwp.northeastern.edu/ns/yaps">
  <teiHeader>
    <fileDesc>
      <titleStmt>
	<title>A first dozen XPath functions</title>
	<author>David J. Birnbaum</author>
      </titleStmt>
      <editionStmt>
	<edition>Introduction to XSLT for Digital Humanities, Brown University, 08-10 March 2012</edition>
      </editionStmt>
      <publicationStmt>
        <distributor>Women Writers Project (via website)</distributor>
        <address>
          <addrLine>url:mailto:wwp@northeastern.edu</addrLine>
        </address>
        <date when="2012-03-09"/>
	<availability status="restricted">
	  <p>Copyright 2011 David J. Birnbaum and the Women Writers Project</p>
	  <p>This TEI-encoded XML file is available under the terms of the <ref target="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons
	  Attribution-ShareAlike 3.0 (Unported)</ref> license.</p>
	</availability>
	<pubPlace>Providence, RI USA</pubPlace>
      </publicationStmt>
      <sourceDesc>
	<p>Covers a few basic XPath 2.0 functions.</p>
      </sourceDesc>
    </fileDesc>
    <revisionDesc>
      <change who="#sdb" when="2013-05-29">
	Per phone conversation with David, remove <code>not()</code>, as we've already covered it
      </change>
      <change who="#sdb" when="2013-05-04">
	Per phone conversation with David:
	<list>
	  <item>nuke <code>count()</code></item>
	  <item>add <code>not()</code>, <code>concat()</code>, and <code>last()</code></item>
	</list>
      </change>
      <change who="#mholmes" when="2011-05-30"> added more answers as comments. </change>
      <change who="#sdb" when="2011-03-21"> tweaked header </change>
      <change who="#djb" when="2011-03-20"> begun </change>
    </revisionDesc>
  </teiHeader>
  <text>
    <presentation>
      <section>
	<head>A first dozen XPath functions</head>
	<slide>
	  <list>
	    <item>Already covered
	    <list>
	      <item><code>count()</code> <bibl>Kay 733</bibl></item>
	      <item><code>not()</code></item>
	    </list>
	    </item>
	    <item>Functions that operate on strings
	    <list>
	      <item><code>normalize-space()</code> <bibl>Kay 845</bibl></item>
	      <item><code>string-length()</code> </item>
	      <item><code>replace()</code> <bibl>Kay 862</bibl></item>
	      <item><code>translate()</code> <bibl>Kay 897</bibl></item>
	      <item><code>concat()</code> </item>
	    </list>
	    </item>
	    <item>functions that operate on sets of numbers<list>
	    <item><code>avg()</code> <bibl>Kay 718</bibl></item>
	    <item><code>max()</code> <bibl>Kay 830</bibl></item>
	    <item><code>min()</code> <bibl>Kay 830</bibl></item>
	    <item><code>sum()</code> <bibl>Kay 889</bibl></item>
	  </list>
	    </item>
	    <item><code>distinct-values()</code> <bibl>Kay 749</bibl></item>
	    <item><code>position()</code> and <code>last()</code> <bibl>Kay 854</bibl></item>
	    <item><code>current-date()</code> <bibl>Kay 738</bibl></item>
	  </list>
	</slide>
      </section>

      <!-- section>
	<head><code>not()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Syntax: <code>not( boolean )</code></item>
	    <item>Semantic: reverses boolean truth value</item>
	    <item>Example: <code>match="persName[ not( surname )]"</code></item>
	  </list>
	</slide>
      </section -->

      <section>
	<head><code>normalize-space()</code> and <code>string-length()</code></head>
	<slide>
	  <list>
	    <item>Trims leading and trailing whitespace; reduces runs
	    of any remaining space to single blank</item>
	    <item>Example:<list>
	    <item><code>string-length( //l[@xml:id eq 'sha-ham301055']/text() )</code> → 70</item>
	    <item><code>string-length( normalize-space( //l[@xml:id eq 'sha-ham301055']/text() ) )</code> → 42</item>
	    </list></item>
	    <item>Uses<list>
	    <item>Clean user input</item>
	    </list></item>
	  </list>
	</slide>
      </section>

      <section>
	<head><code>replace()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Syntax: <code>replace(input-string, regex-pattern, replacement-string,
	    flags)</code></item>
	    <item>Example: <code>replace('Syd','y','i')</code></item>
	    <item>Task: Retrieve all roles in <title>Hamlet</title> and replace “a” with
	    “Hi, Mom!” <list>
	    <item>Complication: What’s wrong with <code>replace(//role,'a','Hi,
	    Mom!')?</code></item>
	    <item>Clever trick: Use an path expression with a dot to refer to
	    the context node, e.g., <code>//role/upper-case(.)</code> means
	    “find all the <gi>role</gi> elements and then upper-case the
	    context node (the <gi>role</gi> element you just found)”</item>
	    </list></item>
	    <!--<item>Solution: //role/replace(.,'a','Hi, Mom!')</item>-->
	    <item>Uses<list>
	    <item>Regex patterns (coming later), such as “transform all dates
	    like MM/DD/YYYY to YYYY-MM-DD”</item>
	    <item>One-to-many or many-to-one transformations (e.g., change LC
	    Cyrillic romanization of “ч” as “ch” into linguistic “č” [or
	    vice versa])</item>
	    </list></item>
	  </list>
	</slide>
      </section>

      <section>
	<head><code>translate()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Syntax: <code>translate(input-string, characters-to-match,
	    replacement-characters)</code></item>
	    <item>Limitation: performs only one-for-one replacements (use
	    <code>replace()</code> for more complex replacements)</item>
	    <item>Example:
	    <code>translate('Syd','ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')</code>
	    <list>
	      <item>Cf. <code>upper-case()</code> and
	      <code>lower-case()</code></item>
	    </list></item>
	    <item>Task: Replace all vowels (aeiou) in the names of <title>Hamlet</title>
	    characters with exclamation marks</item>
	    <!--<item>Solution: //role/translate(.,'AaEeIiOoUu','!!!!!!!!!!')</item>-->
	    <item>Uses<list>
	    <item>One-to-one transformations</item>
	    <item>Change unix paths to Windows paths</item>
	    <item>Change European numerical punctuation to US</item>
	    <item>Convert text from legacy character sets to Unicode</item>
	    </list></item>
	  </list>
	</slide>
      </section>

      <section>
	<head><code>concat()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Syntax: <code>concat( str1, str2, str3 )</code> → str1str2str3</item>
	    <item>Simple example: <code>concat('David has ', $npt, '
	    penguin ties')</code></item>
	    <item>Complex example: <code>concat('Syd has ', if ($npt &gt;
	    0) then $npt else 'no', ' penguin tie', if ($npt &gt; 1) then
	    's' else '')</code></item>
	  </list>
	</slide>
      </section>

      <section>
	<head><code>avg()</code>, <code>max()</code>, <code>min()</code>,
	<code>sum()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Syntax: <code>avg( sequence-of-numbers )</code></item>
	    <item>Example: <code>avg( (1,2,3,4,5,6) )</code><list>
	    <item>What’s wrong with: <code>avg( 1,2,3,4,5,6 )</code>?</item>
	    </list></item>
	    <item>Similarly: <code>max()</code>, <code>min()</code>, and
	    <code>sum()</code></item>
	    <item>Uses<list>
	    <item>Basic arithmetic operations on sets of numerical data
	    items</item>
	    </list></item>
	    <item>Task: Find the average length of a speech in <title>Hamlet</title>,
	    and then the longest and shortest lengths</item>
	    <item>Hints<list>
	    <item>The function <code>string-length(string)</code> returns the
	    length of the string</item>
	    <item>The dot (<code>.</code>) refers to the current item</item>
	    <item><code>//sp/string-length(.)</code> will return a sequence
	    consisting of the length of each string (try it)</item>
	    </list></item>
	    <!--<item>Solution: avg(//sp/string-length(.))</item>-->
	  </list>
	</slide>
      </section>
      <section>
	<head><code>distinct-values()</code>: Roles in (<ref target="../../demos/xslt_intro/ham.xml"><name type="file">ham.xml</name></ref>)</head>
	<slide>
	  <list type="incremental">
	    <item><code>distinct-values(//speaker)</code><list>
	    <item>Returns 38 items</item>
	    <item>What does <code>//speaker</code> return?</item>
	    </list></item>
	    <item><code>//role</code><list>
	    <item>Returns 37 items</item>
	    <item>What does <code>distinct-values(//role)</code> return?</item>
	    <item>Food for thought: What does the discrepancy between speakers
	    and roles mean?</item>
	    <!--<item>Solution: Not all roles speak, some speakers are not single
		role values</item>-->
	    </list></item>
	  </list>
	</slide>
      </section>
      <section>
	<head><code>position()</code> and <code>last()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Returns the position of an item in a sequence</item>
	    <item>Syntax: <code>item/position()</code></item>
	    <item>Example: <code>//role/position()</code> returns the number of each
	    role in the list of roles</item>
	    <item>Task: find the stage direction in the last line Hamlet speaks</item>
	    <item>Note: the <code>last()</code> function returns the
	    sequence number of the last node in the set</item>
	    <!-- Answer: (//sp[contains(@who,'Hamlet')]//l)[position() eq last()]/stage -->
	    <!-- Answer: (//sp[@who eq 'Hamlet']//l)[position() eq last()]/stage when you-->
	    <!-- know he's speaking on his own-->
	  </list>
	</slide>
      </section>

      <section>
	<head><code>current-date()</code></head>
	<slide>
	  <list type="incremental">
	    <item>Syntax: <code>current-date()</code></item>
	    <item>Returns the current date; useful for date-stamping</item>
	    <item>Cf. <code>current-dateTime()</code>,
	    <code>current-time()</code></item>
	    <item>May be used with <code>format-date()</code> (<code>-dateTime</code>,
	    <code>-time</code>) to massage output <bibl>Kay 781</bibl> <list>
	    <item><code>format-date()</code> works only in XSLT, not in the
	    XPath browser of &lt;oXygen/&gt;</item>
	    </list></item>
	    <item>Example: <code>current-date()</code></item>
	    <item>Example: open and run <ref target="../../demos/xslt_intro/djb_calendar.xsl"><name type="file">djb_calendar.xsl</name></ref> in the &lt;oXygen/&gt;
	    XSLT debugger (use any XML file as the source)</item>
	  </list>
	</slide>
      </section>

    </presentation>
  </text>
</TEI>
