स्काला के लिए स्वचालित संसाधन प्रबंधन विकल्प क्या हैं?


102

मैंने स्काला के लिए वेब पर एआरएम (स्वचालित संसाधन प्रबंधन) के कई उदाहरण देखे हैं। यह एक लिखने के लिए एक अनुष्ठान-मार्ग प्रतीत होता है, हालांकि अधिकांश एक दूसरे की तरह बहुत ज्यादा दिखते हैं। मैं किया था , निरंतरता का उपयोग कर एक बहुत अच्छा उदाहरण देख हालांकि।

किसी भी दर पर, बहुत सारे कोड में एक प्रकार या किसी अन्य की खामियां होती हैं, इसलिए मुझे लगा कि स्टैक ओवरफ्लो पर यहां एक संदर्भ रखना एक अच्छा विचार होगा, जहां हम सबसे सही और उपयुक्त संस्करणों को वोट कर सकते हैं।


अगर यह एक समुदाय विकि नहीं होता तो क्या यह प्रश्न और अधिक उत्तर उत्पन्न करता? कृपया ध्यान दें कि अगर समुदाय विकी पुरस्कार प्रतिष्ठा में मतदान के जवाब ...
huynhjl

2
अद्वितीय संदर्भ सुरक्षा के एक अन्य स्तर को एआरएम में जोड़ सकते हैं ताकि यह सुनिश्चित किया जा सके कि संसाधनों के संदर्भ प्रबंधक के पास वापस लौटने से पहले () कहलाते हैं। thread.gmane.org/gmane.comp.lang.scala/19160/focus=19168
retronym

@retronym मुझे लगता है कि विशिष्टता प्लगइन काफी क्रांति होगी, निरंतरता से अधिक। और, वास्तव में, मुझे लगता है कि स्काला में यह एक ऐसी चीज है जो बहुत अधिक विकृत भविष्य में खुद को अन्य भाषाओं में पोर्ट करने की संभावना है। जब यह सामने आता है, तो उसके अनुसार उत्तरों को संपादित करना सुनिश्चित करें। :-)
डैनियल सी। सोबराल

1
क्योंकि मुझे कई java.lang.AutoCloseable उदाहरणों को घोंसला करने में सक्षम होने की आवश्यकता है, जिनमें से प्रत्येक पूर्ववर्ती सफलतापूर्वक इंस्टेंटिंग पर निर्भर करता है, मैं अंत में एक पैटर्न पर हिट करता हूं जो मेरे लिए बहुत उपयोगी रहा है। मैंने इसे इसी तरह के
स्टैकऑवरफ्लो

जवाबों:


10

अभी के लिए Scala 2.13 ने आखिरकार समर्थन किया है: :) काtry with resources उपयोग करके , उदाहरण:

val lines: Try[Seq[String]] =
  Using(new BufferedReader(new FileReader("file.txt"))) { reader =>
    Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
  }

या Using.resourceसे बचने का उपयोग करTry

val lines: Seq[String] =
  Using.resource(new BufferedReader(new FileReader("file.txt"))) { reader =>
    Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
  }

आप डॉक्टर का उपयोग करने से अधिक उदाहरण पा सकते हैं ।

स्वचालित संसाधन प्रबंधन करने के लिए एक उपयोगिता। यह संसाधनों का उपयोग करके एक ऑपरेशन करने के लिए इस्तेमाल किया जा सकता है, जिसके बाद यह संसाधनों को उनके निर्माण के रिवर्स ऑर्डर में जारी करता है।


क्या आप कृपया Using.resourceवैरिएंट भी जोड़ सकते हैं ?
डैनियल सी। सोबरल

@ DanielC.Sobral, यकीन है, बस इसे जोड़ा।
चेंगपोही

आप इसे स्कला 2.12 के लिए कैसे लिखेंगे? यहाँ एक समान usingविधि है:def using[A <: AutoCloseable, B](resource: A) (block: A => B): B = try block(resource) finally resource.close()
माइक स्लैन

75

क्रिस हेन्सन की ब्लॉग प्रविष्टि 'एआरएम ब्लॉक्स इन स्काला: रिविजिटेड' 3/26/09 से मार्टिन ओडस्की की FOSDEM प्रस्तुति की स्लाइड 21 के बारे में बात करती है । यह अगला ब्लॉक स्लाइड 21 (अनुमति के साथ) से सीधे लिया जाता है:

def using[T <: { def close() }]
    (resource: T)
    (block: T => Unit) 
{
  try {
    block(resource)
  } finally {
    if (resource != null) resource.close()
  }
}

- भेजें बोली--

तब हम इस तरह से कॉल कर सकते हैं:

using(new BufferedReader(new FileReader("file"))) { r =>
  var count = 0
  while (r.readLine != null) count += 1
  println(count)
}

इस दृष्टिकोण की कमियां क्या हैं? यह पैटर्न मुझे 95% पता होगा जहाँ मुझे स्वचालित संसाधन प्रबंधन की आवश्यकता होगी ...

संपादित करें: जोड़ा गया कोड स्निपेट


Edit2: डिज़ाइन पैटर्न का विस्तार करना - अजगर के withबयान और पते से प्रेरणा लेना :

  • ब्लॉक से पहले चलाने के लिए बयान
  • प्रबंधित संसाधन के आधार पर अपवाद को फिर से फेंकना
  • एक बयान का उपयोग करके एक के साथ दो संसाधनों को संभालना
  • संसाधन-विशिष्ट हैंडलिंग एक अंतर्निहित रूपांतरण और एक Managedवर्ग प्रदान करके

यह स्काला 2.8 के साथ है।

trait Managed[T] {
  def onEnter(): T
  def onExit(t:Throwable = null): Unit
  def attempt(block: => Unit): Unit = {
    try { block } finally {}
  }
}

def using[T <: Any](managed: Managed[T])(block: T => Unit) {
  val resource = managed.onEnter()
  var exception = false
  try { block(resource) } catch  {
    case t:Throwable => exception = true; managed.onExit(t)
  } finally {
    if (!exception) managed.onExit()
  }
}

def using[T <: Any, U <: Any]
    (managed1: Managed[T], managed2: Managed[U])
    (block: T => U => Unit) {
  using[T](managed1) { r =>
    using[U](managed2) { s => block(r)(s) }
  }
}

class ManagedOS(out:OutputStream) extends Managed[OutputStream] {
  def onEnter(): OutputStream = out
  def onExit(t:Throwable = null): Unit = {
    attempt(out.close())
    if (t != null) throw t
  }
}
class ManagedIS(in:InputStream) extends Managed[InputStream] {
  def onEnter(): InputStream = in
  def onExit(t:Throwable = null): Unit = {
    attempt(in.close())
    if (t != null) throw t
  }
}

implicit def os2managed(out:OutputStream): Managed[OutputStream] = {
  return new ManagedOS(out)
}
implicit def is2managed(in:InputStream): Managed[InputStream] = {
  return new ManagedIS(in)
}

def main(args:Array[String]): Unit = {
  using(new FileInputStream("foo.txt"), new FileOutputStream("bar.txt")) { 
    in => out =>
    Iterator continually { in.read() } takeWhile( _ != -1) foreach { 
      out.write(_) 
    }
  }
}

2
विकल्प हैं, लेकिन मेरा मतलब यह नहीं है कि इसमें कुछ गड़बड़ है। मैं बस उन सभी उत्तरों को यहां चाहता हूं, जो स्टैक ओवरफ्लो पर हैं। :-)
डैनियल सी। सोबरल

5
क्या आपको पता है कि मानक एपीआई में ऐसा कुछ है? हर समय खुद के लिए यह लिखने के लिए एक काम की तरह लगता है।
डैनियल डारबोस

जब इसे पोस्ट किया गया था, तब से कुछ समय पहले हुआ था, लेकिन पहला समाधान भीतरी धारा को बंद नहीं करता है अगर आउट कंस्ट्रक्टर फेंकता है जो शायद यहां नहीं होगा, लेकिन ऐसे अन्य मामले हैं जहां यह खराब हो सकता है। पास भी फेंक सकते हैं। घातक अपवादों के बीच कोई अंतर नहीं है। दूसरे के पास हर जगह कोड की गंध है और पहले के मुकाबले शून्य फायदे हैं। तुम भी वास्तविक प्रकार ढीला तो एक ZipInputStream की तरह कुछ के लिए बेकार हो जाएगा।
स्टेयिनबोट

यदि ब्लॉक पुनरावृत्त लौटाता है तो आप इसे कैसे करने की सलाह देते हैं?
जॉर्ज मैकडो

62

डैनियल,

मैंने अभी हाल ही में स्वचालित संसाधन प्रबंधन के लिए स्काला-आर्म लाइब्रेरी को तैनात किया है। आप यहां दस्तावेज पा सकते हैं: https://github.com/jsuereth/scala-arm/wiki

यह लाइब्रेरी उपयोग की तीन शैलियों का समर्थन करती है (वर्तमान में):

1) शाही / अभिव्यक्ति के लिए:

import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}

2) मोनडिक-शैली

import resource._
import java.io._
val lines = for { input <- managed(new FileInputStream("test.txt"))
                  val bufferedReader = new BufferedReader(new InputStreamReader(input)) 
                  line <- makeBufferedReaderLineIterator(bufferedReader)
                } yield line.trim()
lines foreach println

3) सीमांकित निरंतरता-शैली

यहाँ एक "इको" tcp सर्वर है:

import java.io._
import util.continuations._
import resource._
def each_line_from(r : BufferedReader) : String @suspendable =
  shift { k =>
    var line = r.readLine
    while(line != null) {
      k(line)
      line = r.readLine
    }
  }
reset {
  val server = managed(new ServerSocket(8007)) !
  while(true) {
    // This reset is not needed, however the  below denotes a "flow" of execution that can be deferred.
    // One can envision an asynchronous execuction model that would support the exact same semantics as below.
    reset {
      val connection = managed(server.accept) !
      val output = managed(connection.getOutputStream) !
      val input = managed(connection.getInputStream) !
      val writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output)))
      val reader = new BufferedReader(new InputStreamReader(input))
      writer.println(each_line_from(reader))
      writer.flush()
    }
  }
}

कोड एक संसाधन प्रकार-विशेषता का उपयोग करता है, इसलिए यह अधिकांश संसाधन प्रकारों को अनुकूलित करने में सक्षम है। इसमें एक करीबी या डिस्पोज़ विधि के साथ कक्षाओं के खिलाफ संरचनात्मक टाइपिंग का उपयोग करने के लिए एक कमबैक है। कृपया दस्तावेज़ीकरण देखें और मुझे बताएं कि क्या आप जोड़ने के लिए किसी भी उपयोगी सुविधाओं के बारे में सोचते हैं।


1
हां, मैंने यह देखा। मैं कोड देखना चाहता हूं, यह देखने के लिए कि आप कुछ चीजों को कैसे पूरा करते हैं, लेकिन मैं अभी बहुत व्यस्त हूं। वैसे भी, चूंकि प्रश्न का लक्ष्य विश्वसनीय एआरएम कोड का संदर्भ प्रदान करना है, इसलिए मैं इसे स्वीकृत उत्तर दे रहा हूं।
डैनियल सी। सोबरल

18

निरंतरता का उपयोग करते हुए यहां जेम्स आईरी समाधान है:

// standard using block definition
def using[X <: {def close()}, A](resource : X)(f : X => A) = {
   try {
     f(resource)
   } finally {
     resource.close()
   }
}

// A DC version of 'using' 
def resource[X <: {def close()}, B](res : X) = shift(using[X, B](res))

// some sugar for reset
def withResources[A, C](x : => A @cps[A, C]) = reset{x}

तुलना के साथ और बिना निरंतरता के समाधान यहां दिए गए हैं:

def copyFileCPS = using(new BufferedReader(new FileReader("test.txt"))) {
  reader => {
   using(new BufferedWriter(new FileWriter("test_copy.txt"))) {
      writer => {
        var line = reader.readLine
        var count = 0
        while (line != null) {
          count += 1
          writer.write(line)
          writer.newLine
          line = reader.readLine
        }
        count
      }
    }
  }
}

def copyFileDC = withResources {
  val reader = resource[BufferedReader,Int](new BufferedReader(new FileReader("test.txt")))
  val writer = resource[BufferedWriter,Int](new BufferedWriter(new FileWriter("test_copy.txt")))
  var line = reader.readLine
  var count = 0
  while(line != null) {
    count += 1
    writer write line
    writer.newLine
    line = reader.readLine
  }
  count
}

और यहाँ सुधार के Tiark Rompf सुझाव है:

trait ContextType[B]
def forceContextType[B]: ContextType[B] = null

// A DC version of 'using'
def resource[X <: {def close()}, B: ContextType](res : X): X @cps[B,B] = shift(using[X, B](res))

// some sugar for reset
def withResources[A](x : => A @cps[A, A]) = reset{x}

// and now use our new lib
def copyFileDC = withResources {
 implicit val _ = forceContextType[Int]
 val reader = resource(new BufferedReader(new FileReader("test.txt")))
 val writer = resource(new BufferedWriter(new FileWriter("test_copy.txt")))
 var line = reader.readLine
 var count = 0
 while(line != null) {
   count += 1
   writer write line
   writer.newLine
   line = reader.readLine
 }
 count
}

का उपयोग नहीं करता है (नया बफ़रड्राइवर (नया FileWriter ("test_copy.txt"))) जब बफ़रड्राइवर कंस्ट्रक्टर विफल हो जाता है, तो समस्याओं का सामना करना पड़ता है? प्रत्येक संसाधन का उपयोग ब्लॉक में लपेटा जाना चाहिए ...
जाप

@ जैप यह ओरेकल द्वारा सुझाई गई शैली है । BufferedWriterचेक किए गए अपवादों को न फेंके, इसलिए यदि कोई अपवाद फेंका जाए, तो प्रोग्राम को इससे उबरने की उम्मीद नहीं है।
डेनियल सी। सोबरल

7

मैं स्काला में एआरएम करने के लिए एक क्रमिक 4 चरण विकास देख रहा हूं:

  1. एआरएम नहीं: गंदगी
  2. केवल क्लोजर: बेहतर, लेकिन कई नेस्टेड ब्लॉक
  3. कंटीन्यूएशन मोनाड: नेस्टिंग को समतल करने के लिए उपयोग करें, लेकिन 2 ब्लॉक में अप्राकृतिक पृथक्करण
  4. प्रत्यक्ष शैली निरंतरता: नीरव, अहा! यह भी सबसे प्रकार-सुरक्षित विकल्प है :ResRes ब्लॉक के साथ एक संसाधन टाइप त्रुटि होगी।

1
आप पर ध्यान दें, स्काला में CPS को मोनाड्स के माध्यम से लागू किया जाता है। :-)
डैनियल सी। सोबरल

1
मुश्ताक, 3) आप संसाधन प्रबंधन एक ऐसे साधु में कर सकते हैं जो निरंतरता का सनक नहीं है। 4) संसाधन प्रबंधन मेरे साथ उपयोग कर रहा है / संसाधन सीमांकित निरंतरता कोड कोई और (और कम नहीं) प्रकार है जिसका उपयोग "सुरक्षित है।" यह अभी भी संभव है कि एक संसाधन का प्रबंधन करना भूल जाए। तुलना (नए संसाधन ()) {पहले => वैल सेकंड = नए संसाधन () // उफ़! // उपयोग संसाधन} // केवल पहले बंद हो जाता है स्रोत के साथ {वैल पहले = संसाधन (नया संसाधन ()) वैल दूसरा = नया संसाधन () // पुलिस! // उपयोग संसाधनों ...} // केवल पहले बंद हो जाता है
जेम्स आइरी

2
डैनियल, स्काला में सीपीएस किसी भी कार्यात्मक भाषा में सीपीएस की तरह है। यह सीमांकित का उपयोग करने वाली निरंतरता है।
जेम्स इरी

जेम्स, इसे अच्छी तरह से समझाने के लिए धन्यवाद। भारत में बैठकर मैं केवल यह चाह सकता था कि मैं आपके BASE बात के लिए वहाँ था। प्रतीक्षा है कि जब आप उन स्लाइडों को ऑनलाइन डालते हैं :)
मुश्ताक अहमद

6

बेहतर फाइलों के साथ शामिल लाइट-वेट (कोड की 10 लाइनें) एआरएम है। देखें: https://github.com/pathikrit/better-files#lightweight-arm

import better.files._
for {
  in <- inputStream.autoClosed
  out <- outputStream.autoClosed
} in.pipeTo(out)
// The input and output streams are auto-closed once out of scope

यदि आप संपूर्ण पुस्तकालय नहीं चाहते हैं तो यहां यह लागू किया गया है:

  type Closeable = {
    def close(): Unit
  }

  type ManagedResource[A <: Closeable] = Traversable[A]

  implicit class CloseableOps[A <: Closeable](resource: A) {        
    def autoClosed: ManagedResource[A] = new Traversable[A] {
      override def foreach[U](f: A => U) = try {
        f(resource)
      } finally {
        resource.close()
      }
    }
  }

यह बहुत अच्छा है। मैं इस दृष्टिकोण के समान कुछ ले गया, लेकिन एक mapऔर flatMapफॉर्सेबल ऑप्स के लिए विधि को फॉर्च्यूनर के बजाय परिभाषित किया ताकि समझ के लिए एक ट्रैवर्सेबल उपज न हो।
एजकैसेबर्ग

1

टाइप कक्षाओं का उपयोग करने के बारे में कैसे

trait GenericDisposable[-T] {
   def dispose(v:T):Unit
}
...

def using[T,U](r:T)(block:T => U)(implicit disp:GenericDisposable[T]):U = try {
   block(r)
} finally { 
   Option(r).foreach { r => disp.dispose(r) } 
}

1

एक और विकल्प है चॉपी का लज़ीज़ ट्राईक्लोज़ मोनड। यह डेटाबेस कनेक्शन के साथ बहुत अच्छा है:

val ds = new JdbcDataSource()
val output = for {
  conn  <- TryClose(ds.getConnection())
  ps    <- TryClose(conn.prepareStatement("select * from MyTable"))
  rs    <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))

// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
    case Success(result) => // Do something
    case Failure(e) =>      // Handle Stuff
}

और धाराओं के साथ:

val output = for {
  outputStream      <- TryClose(new ByteArrayOutputStream())
  gzipOutputStream  <- TryClose(new GZIPOutputStream(outputStream))
  _                 <- TryClose.wrap(gzipOutputStream.write(content))
} yield wrap({gzipOutputStream.flush(); outputStream.toByteArray})

output.resolve.unwrap match {
  case Success(bytes) => // process result
  case Failure(e) => // handle exception
}

अधिक जानकारी यहाँ: https://github.com/choppythelumberjack/tryclose


0

यहाँ @ चेंगपोही का जवाब है, संशोधित किया गया है, इसलिए यह स्कैला २.३+ के साथ काम करता है, बजाय केवल २.१३ के बजाय (हाँ, यह २.१३ स्कैला के साथ भी काम करता है):

def unfold[A, S](start: S)(op: S => Option[(A, S)]): List[A] =
  Iterator
    .iterate(op(start))(_.flatMap{ case (_, s) => op(s) })
    .map(_.map(_._1))
    .takeWhile(_.isDefined)
    .flatten
    .toList

def using[A <: AutoCloseable, B](resource: A)
                                (block: A => B): B =
  try block(resource) finally resource.close()

val lines: Seq[String] =
  using(new BufferedReader(new FileReader("file.txt"))) { reader =>
    unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
  }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.