christopher clark
christopher clark

Reputation: 2103

Passing multiple arguments into Predicate, Functional Programming

I am trying to better understand functional Programming. The example below was provided, this example creates a predicate lambda. Then creates a static method to Count the number of items that pass the predicate conditions.

public Predicate<string> longWords = (x) => x.Length > 10;

public static int Count<T>(T[] array, Predicate<T> predicate, int counter)
    {
        for (int i = 0; i < array.Length; i++)
        {
            if (predicate(array[i]))
            {
                counter++;
            }
        }

        return counter;
    }

I wanted to make this solution follow the Referential Transparency principle which states that you can determine the result of applying that function only by looking at the values of its arguments.

Unfortunately longWords doesn't tell me what a long word really means, I need to pass in an argument to longWords telling it the length that makes a function a long word, ie not hardcoding '10'.

Predicates only take in one parameter, and making longWords require Predicate<Tuple<string,int>>, comes with its challenges.

Below is my attempt at coming to a solution, and the two error messages that resulted.

public class BaseFunctions
{
    public Predicate<Tuple<string, int>> longWords = (x) => x.Item1.Length > x.Item2;

    public static int Count<T>(T[] array, Predicate<T> predicate, int counter)
    {
        for (int i = 0; i < array.Length; i++)
        {
            /// error below: Cannot convert from 'T' to 'string'
            /// in the example provided predicate(array[i]) worked fine when the predicate had only string type
            if (predicate(new Tuple<string, int>(array[i], 10)))
            {
                counter++;
            }
        }
        return counter;
    }
}

and it's usage

 BaseFunctions b = new BaseFunctions();
 string[] text = ["This is a really long string", "Not 10"];
 /// also error below on BaseFunctions.Count 'The type arguments for method BaseFunctions.Count<T>(T[], Predicate<T>, int) cannot be inferred from the usage. Try specifying the arguments explicitly
 Console.WriteLine(BaseFunctions.Count(text, b.longWords, 0).ToString());
 Console.ReadLine();

Upvotes: 3

Views: 5170

Answers (1)

Ousmane D.
Ousmane D.

Reputation: 56473

I'd do:

public Func<string, int, bool> longWords = (str, number) => str.Length > number;

instead of:

public Predicate<Tuple<string, int>> longWords = (x) => x.Item1.Length > x.Item2;

This is simply because I believe it's easier to work with a function that takes a string and an int then returns a bool as opposed to a predicate that takes a tuple of string and int and returns a bool. plus this version of the tuple type is cumbersome to work with. consider the new tuple types if you're using C#7 and above.

Then change the method signature from:

public static int Count<T>(T[] array, Predicate<T> predicate, int counter)

to:

public static int Count<T>(T[] array, Func<T, int, bool> predicate, int counter)

To accommodate the change of delegate types mentioned above.

Then the if condition needs to be changed from:

if (predicate(new Tuple<string, int>(array[i], 10)))

to:

if (predicate(array[i], 10))

To accommodate the change of delegate types mentioned above.

Also, note that in C# you'll need to define the array like this:

string[] text = { "This is a really long string", "Not 10" };

and not with the [ ] braces.

Now your usage code becomes:

BaseFunctions b = new BaseFunctions();
string[] text = { "This is a really long string", "Not 10" };

Console.WriteLine(BaseFunctions.Count(text, b.longWords, 0).ToString());
Console.ReadLine();

Upvotes: 4

Related Questions