Predicates, actions and delegates

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • balabaster
    Recognized Expert Contributor
    • Mar 2007
    • 798

    Predicates, actions and delegates

    Okay, after having bashed my head against this long enough I thought of you guys... I thought having exhausted just about every other .NET concept I would come back to predicates because Microsoft's "EndsWithSaurus " demonstration is beyond useless at explaining what this is useful for. Having previously understood delegates and got my head around those, predicates finally makes sense...althoug h I still think Microsoft's example is ridiculous. I still can't make head nor tail of what they would use such an example for...shouldn't you be able to pass a variable into the predicate for the search instead of having to hard-code the search string? Anyway...moving on...

    So on to actions...Actio n<T>: Array.ForEach appears to use Action<T> which is the same conceptually as Predicate<T> except that it now tells us to do something instead of just find out if we've got a match.

    I threw a 30 second example together that I thought should work...but for the life of me I can't figure out what I'm missing!

    [Code=vbnet]Function IsPrime(ByVal Data As Integer) As Boolean
    If IsEven(Data) Then Return False
    If Data <= 3 Then Return True
    For i As Integer = 3 To CInt(Math.Sqrt( Data)) Step 2
    If (Data Mod i = 0) Then Return False
    Next
    Return True
    End Function

    Public Sub AddOne(ByVal Data As Integer)
    Data += 1
    End Sub

    Private Sub Main()

    'Compose some array of integers
    Dim AllValues() As Integer = New Integer() {1, 3, 5, 6, 8, 9, 10, 12, 13, 15, 17}
    'Grab just the prime numbers from our array
    Dim MyPredicate As New Predicate(Of Integer)(Addres sOf IsPrime)
    Dim FilteredValues( ) As Integer = Array.FindAll(A llValues, MyPredicate)

    'Do something with each item in our filtered values list
    Dim MyAction As New Action(Of Integer)(Addres sOf AddOne)
    Array.ForEach(O f Integer)(Filter edValues, MyAction)

    'Grab our newly modified data
    Dim ModifiedValues( ) As Integer = FilteredValues
    End Sub[/Code]
    So stepping through the code, it would appear that my predicate is running on my array and filtering the primes for me just as I expect...but my action is not adding one to my filtered values...when I get to the end, I would expect modified values to have a list of prime numbers incremented by 1, but instead, I have the list of raw prime numbers.

    I'm obviously missing something, but I can't for the life of me see what it is...
  • Plater
    Recognized Expert Expert
    • Apr 2007
    • 7872

    #2
    Well you pass your parameter "byVal" and expect changes you make inside the function to affect the passed in val.
    Don't you need to use "ByRef" or whatever VB calls it?

    Comment

    • balabaster
      Recognized Expert Contributor
      • Mar 2007
      • 798

      #3
      Originally posted by Plater
      Well you pass your parameter "byVal" and expect changes you make inside the function to affect the passed in val.
      Don't you need to use "ByRef" or whatever VB calls it?
      You would think that would be it, and truth be known, I already tried that thinking exactly the same thing, but then I get a compilation error because my action doesn't match the required signature...whi ch doesn't specify which form it should take. I thought maybe if I just removed the byref/byval indicator that it would fix it, but it automatically put it back as byval, compiled and then didn't work.

      So yeah...I don't think that's it...any other thoughts?

      Comment

      • balabaster
        Recognized Expert Contributor
        • Mar 2007
        • 798

        #4
        The more I think about it, the more I wonder what Action<T> was supposed to be used for. It doesn't appear you can do any useful manipulation of data in an array with it. It's sole purpose seems directed towards carrying out some repetitive external action that requires the array data as an input, where 3 lines of code enumerating the array was so horrifying they thought that they absolutely must reduce it to a single line of code (assuming you'd written 5 other lines of code to declare your predicate and your action, or taken the time to understand lamda expressions).

        Maybe someone can direct me to a single useful example for the Action<T> class... even the Predicate<T> class seems limited in its use such that if we never had cause to filter an array, there might never be a use for it.

        Please someone tell me that I'm missing something because there must've been some greater purpose for Action<T> than spitting out message boxes or writing the contents of an array to a log or the window. I'd like to think that the .NET team weren't asleep at the wheel when this concept was included in the framework...

        Comment

        • Plater
          Recognized Expert Expert
          • Apr 2007
          • 7872

          #5
          Can it return a value?

          Comment

          • balabaster
            Recognized Expert Contributor
            • Mar 2007
            • 798

            #6
            Originally posted by Plater
            Can it return a value?
            Nope, it doesn't seem to be able to do that.

            I've done some more research since I posted this question, and it seems that maybe I missed the purpose of the Action<T> predicate.

            It seems that the purpose I presumed it should be of value for, it really isn't, and that was for iterating through the array and somehow modifying the content and returning the modified array - which would be by far the most useful thing it could do in my opinion.

            However, it seems that its purpose was misunderstood. It's real purpose appears to be rather for doing some action as a result of the data that is passed in - for instance, writing to an event log, writing to a file, outputting data to screen, populating some class level variable (maybe an array) or doing some other task that requires the data from the array, but cannot modify the data in that array for the purpose of returning the modified data.

            Does that make sense?

            The example below demonstrates:
            [Code=vb]Public Sub WriteToConsole( ByVal Data As String)
            Console.WriteLi ne(Data)
            End Sub

            Sub Main()

            Dim oItems() As String = { _
            "George Washington", _
            "John Adams", _
            "Thomas Jefferson", _
            "James Madison", _
            "James Monroe" _
            }

            Array.ForEach(o Items, New Action(Of String)(Address Of WriteToConsole)

            Console.ReadLin e()

            End Sub[/Code]

            Comment

            • Plater
              Recognized Expert Expert
              • Apr 2007
              • 7872

              #7
              Hmm. Does that effectively create a "Queuing" process that can happen as a background thread?
              Like currently for a test project I am reading things that are sent to me over a serial port. It comes in at a decent enough speed.
              When I reach a deliminator character I form a message and push it on a queue. Then I have a seperate thread cycling through that Queue object and logging the data to a file.

              If that action thing would handle the task of firing an event "when time is avialable" to trigger that log to disk, that would be pretty good.

              Comment

              • balabaster
                Recognized Expert Contributor
                • Mar 2007
                • 798

                #8
                Originally posted by Plater
                If that action thing would handle the task of firing an event "when time is avialable" to trigger that log to disk, that would be pretty good.
                Alas, it doesn't appear that the Queue object has a ForEach method... I imagine there must be some way to add a hack to it to allow it.

                Maybe extend the queue object to implement IEnumerable...i f I recall correctly, the IEnumerable interface has a ForEach method which would allow you to implement the action thing...

                How I've handled that sort of operation in the past is I add a thread to process the queue with a class level ManualReset switch at the start of the thread. When the thread that posts items to the queue has added an item it sets the manual reset, and when the thread that's reading items from the queue has emptied the queue, it sets the manualreset switch to false. Relatively simple I think.

                Thinking about it, I'm not sure you could use the action thing because the queue size would keep changing and you'd get the same error you get when you try to use a for each to delete items from a list.

                Comment

                Working...