Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pluralize function for strings #3992

Merged
merged 2 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/ref/string.xml
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ gap> HexStringInt(last);
<#Include Label="Ordinal">
<#Include Label="EvalString">
<#Include Label="CrcString">
<#Include Label="Pluralize">

</Section>

Expand Down
65 changes: 65 additions & 0 deletions lib/string.gd
Original file line number Diff line number Diff line change
Expand Up @@ -939,3 +939,68 @@ DeclareGlobalFunction("StringOfMemoryAmount");
DeclareGlobalFunction("StringFormatted");
DeclareGlobalFunction("PrintFormatted");
DeclareGlobalFunction("PrintToFormatted");


#############################################################################
##
## <#GAPDoc Label="Pluralize">
## <ManSection>
## <Func Name="Pluralize" Arg='[count, ]string[, plural]'/>
## <Returns>A string</Returns>
##
## <Description>
## This function returns an attempt at the appropriate pluralization
## of a string (considered as a singular English noun), using several
## rules and heuristics of English grammar.
## <P/>
##
## The arguments to this function are an optional non-negative
## integer <A>count</A> (the number of objects in question),
## a non-empty string <A>string</A>
## (the singular form of the object in question),
## and an optional additional string <A>plural</A>
## (the plural form of <A>string</A>).
## <P/>
##
## If <A>plural</A> is given, then <C>Pluralize</C> uses it as the
## plural form of <A>string</A>, otherwise <C>Pluralize</C>
## makes an informed guess at the plural.
## <P/>
##
## If <A>count</A> is not given, then <C>Pluralize</C> returns this
## plural form of <A>string</A>.
## If <A>count</A> is given and has value <M>n \neq 1</M>,
## then this string is prepended by "\&gt;n\&lt; ";
## else if <A>count</A> has value <M>1</M>, then <C>Pluralize</C>
## returns <A>string</A>, prepended by "\&gt;1\&lt; ".
## <P/>
##
## Note that <Ref Func="StripLineBreakCharacters" /> can be used to
## remove the control characters <C>\&lt;</C> and <C>\&gt;</C> from
## the return value.
##
## </Description>
## </ManSection>
## <Example><![CDATA[
## gap> Pluralize( "generator" );
## "generators"
## gap> Pluralize( 1, "generator" );
## "\>1\< generator"
## gap> Pluralize( 0, "generator" );
## "\>0\< generators"
## gap> Pluralize( "man", "men" );
## "men"
## gap> Pluralize( 1, "man", "men" );
## "\>1\< man"
## gap> Print( Pluralize( 2, "man", "men" ) );
## 2 men
## gap> Print( Pluralize( 2, "vertex" ) );
## 2 vertices
## gap> Print( Pluralize( 3, "matrix" ) );
## 3 matrices
## gap> Print( Pluralize( 4, "battery" ) );
## 4 batteries
## ]]></Example>
## <#/GAPDoc>

DeclareGlobalFunction("Pluralize");
83 changes: 83 additions & 0 deletions lib/string.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1406,3 +1406,86 @@ InstallGlobalFunction(PrintFormatted, function(args...)
# directed
Print(CallFuncList(StringFormatted, args));
end);

InstallGlobalFunction(Pluralize,
function(args...)
local nargs, i, count, include_num, str, len, out;

#Int and one string
#Int and two strings
#One string
#Two strings

nargs := Length(args);
if nargs >= 1 and IsInt(args[1]) and args[1] >= 0 then
i := 2;
count := args[1];
include_num := true;
else
i := 1;
include_num := false; # if not given, assume pluralization is wanted.
fi;

if not (nargs in [i, i + 1] and
IsString(args[i]) and
(nargs = i or IsString(args[i + 1]))) then
ErrorNoReturn("Usage: Pluralize([<count>, ]<string>[, <plural>])");
fi;

str := args[i];
len := Length(str);

if len = 0 then
ErrorNoReturn("the argument <str> must be a non-empty string");
elif include_num and count = 1 then # no pluralization needed
return Concatenation("\>1\< ", str);
elif nargs = i + 1 then # pluralization given
out := args[i + 1];
elif len <= 2 then
out := Concatenation(str, "s");

# Guess and return the plural form of <str>.
# Inspired by the "Ruby on Rails" inflection rules.

# Uncountable nouns
elif str in ["equipment", "information"] then
out := str;

# Irregular plurals
elif str = "axis" then
out := "axes";
elif str = "child" then
out := "children";
elif str = "person" then
out := "people";

# Peculiar endings
elif EndsWith(str, "ix") or EndsWith(str, "ex") then
out := Concatenation(str{[1 .. len - 2]}, "ices");
elif EndsWith(str, "x") then
out := Concatenation(str, "es");
elif EndsWith(str, "tum") or EndsWith(str, "ium") then
out := Concatenation(str{[1 .. len - 2]}, "a");
elif EndsWith(str, "sis") then
out := Concatenation(str{[1 .. len - 3]}, "ses");
elif EndsWith(str, "fe") and not EndsWith(str, "ffe") then
out := Concatenation(str{[1 .. len - 2]}, "ves");
elif EndsWith(str, "lf") or EndsWith(str, "rf") or EndsWith(str, "loaf") then
out := Concatenation(str{[1 .. len - 1]}, "ves");
elif EndsWith(str, "y") and not str[len - 1] in "aeiouy" then
out := Concatenation(str{[1 .. len - 1]}, "ies");
elif str{[len - 1, len]} in ["ch", "ss", "sh"] then
out := Concatenation(str, "es");
elif EndsWith(str, "s") then
out := str;

# Default to appending 's'
else
out := Concatenation(str, "s");
fi;

if include_num then
return Concatenation("\>", String(args[1]), "\< ", out);
fi;
return out;
end);
6 changes: 3 additions & 3 deletions src/calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ static Obj DoWrap0args(Obj self)

/****************************************************************************
**
*F DoWrap1args( <self>, <arg1> ) . . . . . . . wrap up 1 arguments in a list
*F DoWrap1args( <self>, <arg1> ) . . . . . . . wrap up 1 argument in a list
*/
static Obj DoWrap1args(Obj self, Obj arg1)
{
Expand Down Expand Up @@ -333,7 +333,7 @@ static Obj DoFail0args(Obj self)

/****************************************************************************
**
*F DoFail1args( <self>,<arg1> ) . . . fail a function call with 1 arguments
*F DoFail1args( <self>,<arg1> ) . . . fail a function call with 1 argument
*/
static Obj DoFail1args(Obj self, Obj arg1)
{
Expand Down Expand Up @@ -507,7 +507,7 @@ static Obj DoProf0args (

/****************************************************************************
**
*F DoProf1args( <self>, <arg1>) . . . . profile a function with 1 arguments
*F DoProf1args( <self>, <arg1>) . . . . profile a function with 1 argument
*/
static Obj DoProf1args (
Obj self,
Expand Down
2 changes: 1 addition & 1 deletion src/calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ EXPORT_INLINE Obj CALL_XARGS(Obj f, Obj as)
/****************************************************************************
**
*F CALL_0ARGS_PROF( <func>, <arg1> ) . . . . . call a prof func with 0 args
*F CALL_1ARGS_PROF( <func>, <arg1>, ... ) . . call a prof func with 1 args
*F CALL_1ARGS_PROF( <func>, <arg1>, ... ) . . call a prof func with 1 arg
*F CALL_2ARGS_PROF( <func>, <arg1>, ... ) . . call a prof func with 2 args
*F CALL_3ARGS_PROF( <func>, <arg1>, ... ) . . call a prof func with 3 args
*F CALL_4ARGS_PROF( <func>, <arg1>, ... ) . . call a prof func with 4 args
Expand Down
Loading